From 5383c48d3097c222c38339dc06afdbed59932486 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 17 Dec 2025 15:54:13 +0100 Subject: [PATCH 01/25] add noRoute violation --- src/CONST/index.ts | 1 + src/components/ReportActionItem/MoneyRequestView.tsx | 2 ++ src/hooks/useViolations.ts | 3 ++- src/languages/en.ts | 1 + src/libs/Violations/ViolationsUtils.ts | 2 ++ 5 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 01626ba0811b6..7f4f3e29aa0d7 100755 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -5715,6 +5715,7 @@ const CONST = { OVER_TRIP_LIMIT: 'overTripLimit', MISSING_ATTENDEES: 'missingAttendees', COMPANY_CARD_REQUIRED: 'companyCardRequired', + NO_ROUTE: 'noRoute', }, RTER_VIOLATION_TYPES: { BROKEN_CARD_CONNECTION: 'brokenCardConnection', diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index 2723f45b34d86..48ded818f9550 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -596,6 +596,8 @@ function MoneyRequestView({ ROUTES.MONEY_REQUEST_STEP_DISTANCE.getRoute(CONST.IOU.ACTION.EDIT, iouType, transaction.transactionID, report.reportID, getReportRHPActiveRoute()), ); }} + brickRoadIndicator={getErrorForField('waypoints') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} + errorText={getErrorForField('waypoints')} copyValue={distanceCopyValue} copyable={!!distanceCopyValue} /> diff --git a/src/hooks/useViolations.ts b/src/hooks/useViolations.ts index acd96ca0987a8..bbad1d83c2faa 100644 --- a/src/hooks/useViolations.ts +++ b/src/hooks/useViolations.ts @@ -6,7 +6,7 @@ import type {TransactionViolation, ViolationName} from '@src/types/onyx'; /** * Names of Fields where violations can occur. */ -const validationFields = ['amount', 'billable', 'category', 'comment', 'date', 'merchant', 'receipt', 'tag', 'tax', 'attendees', 'customUnitRateID', 'none'] as const; +const validationFields = ['amount', 'billable', 'category', 'comment', 'date', 'merchant', 'receipt', 'tag', 'tax', 'attendees', 'customUnitRateID', 'waypoints', 'none'] as const; type ViolationField = TupleToUnion; @@ -61,6 +61,7 @@ const violationNameToField: Record 'none', receiptGeneratedWithAI: () => 'receipt', companyCardRequired: () => 'none', + noRoute: () => 'waypoints', }; type ViolationsMap = Map; diff --git a/src/languages/en.ts b/src/languages/en.ts index 60df89dca2222..3733cbbc01e1a 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -7217,6 +7217,7 @@ const translations = { hold: 'This expense was put on hold', resolvedDuplicates: 'resolved the duplicate', companyCardRequired: 'Company card purchases required', + noRoute: 'No route was found. Please select a valid address', }, reportViolations: { [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: ({fieldName}: RequiredFieldParams) => `${fieldName} is required`, diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index 5836048a58672..c583497f1ef17 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -620,6 +620,8 @@ const ViolationsUtils = { }); case CONST.VIOLATIONS.RECEIPT_GENERATED_WITH_AI: return translate('violations.receiptGeneratedWithAI'); + case CONST.VIOLATIONS.NO_ROUTE: + return translate('violations.noRoute'); default: // The interpreter should never get here because the switch cases should be exhaustive. // If typescript is showing an error on the assertion below it means the switch statement is out of From 5f7c1ca301fa38ace09e02b048998a2fb1658ac8 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 17 Dec 2025 16:29:21 +0100 Subject: [PATCH 02/25] empty string if distance is zero --- src/libs/DistanceRequestUtils.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/libs/DistanceRequestUtils.ts b/src/libs/DistanceRequestUtils.ts index 1a3f1b119e73f..433ffea2045d7 100644 --- a/src/libs/DistanceRequestUtils.ts +++ b/src/libs/DistanceRequestUtils.ts @@ -175,10 +175,14 @@ function getDistanceForDisplay( translate: LocaleContextProps['translate'], useShortFormUnit?: boolean, ): string { - if (!hasRoute || !unit || !distanceInMeters) { + if (!hasRoute || !unit) { return translate('iou.fieldPending'); } + if (!distanceInMeters) { + return ''; + } + const distanceInUnits = getRoundedDistanceInUnits(distanceInMeters, unit); if (useShortFormUnit) { return `${distanceInUnits} ${unit}`; @@ -219,6 +223,10 @@ function getDistanceMerchant( return translate('iou.fieldPending'); } + if (!distanceInMeters) { + return ''; + } + const distanceInUnits = getDistanceForDisplay(hasRoute, distanceInMeters, unit, rate, translate, true); const ratePerUnit = getRateForDisplay(unit, rate, currency, translate, toLocaleDigit, undefined, true); From 94e015cff16c42aaef3bdfd3e68b87882e91a61f Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 17 Dec 2025 17:25:54 +0100 Subject: [PATCH 03/25] update displayed reportName if no route was found --- src/languages/en.ts | 1 + src/libs/ReportUtils.ts | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/languages/en.ts b/src/languages/en.ts index 3733cbbc01e1a..aff84365cb391 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1199,6 +1199,7 @@ const translations = { fieldPending: 'Pending...', defaultRate: 'Default rate', receiptMissingDetails: 'Receipt missing details', + noRoute: 'No route was found', missingAmount: 'Missing amount', missingMerchant: 'Missing merchant', receiptStatusTitle: 'Scanning…', diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 462ede3eca9b6..ca28abbd8aa94 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -295,6 +295,7 @@ import { isExpensifyCardTransaction, isFetchingWaypointsFromServer, isManualDistanceRequest as isManualDistanceRequestTransactionUtils, + isMapDistanceRequest, isOnHold as isOnHoldTransactionUtils, isPayAtEndExpense, isPending, @@ -5131,6 +5132,12 @@ function getTransactionReportName({ return translateLocal('iou.fieldPending'); } + // If a map distance request has no route (and the distance is zero) the merchant would be empty + // Using the merchant is easier than explicitly checking for distance + if (isMapDistanceRequest(transaction) && !getMerchant(transaction)) { + return translateLocal('iou.noRoute'); + } + if (isSentMoneyReportAction(reportAction)) { return getIOUReportActionDisplayMessage(reportAction as ReportAction, transaction); } From 5cec4272c374a3636cb5e7115b54022d5d1a5c23 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Sat, 20 Dec 2025 21:47:20 +0100 Subject: [PATCH 04/25] update copy --- src/languages/en.ts | 3 +-- src/libs/ReportUtils.ts | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/languages/en.ts b/src/languages/en.ts index 137ade2f4d045..cc8ae5e56faff 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1188,7 +1188,6 @@ const translations = { fieldPending: 'Pending...', defaultRate: 'Default rate', receiptMissingDetails: 'Receipt missing details', - noRoute: 'No route was found', missingAmount: 'Missing amount', missingMerchant: 'Missing merchant', receiptStatusTitle: 'Scanning…', @@ -7233,7 +7232,7 @@ const translations = { hold: 'This expense was put on hold', resolvedDuplicates: 'resolved the duplicate', companyCardRequired: 'Company card purchases required', - noRoute: 'No route was found. Please select a valid address', + noRoute: 'Please select a valid address', }, reportViolations: { [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: ({fieldName}: RequiredFieldParams) => `${fieldName} is required`, diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index d23bd609c932c..0b56623e23578 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5127,10 +5127,11 @@ function getTransactionReportName({ return translateLocal('iou.fieldPending'); } - // If a map distance request has no route (and the distance is zero) the merchant would be empty + // If a map distance request has no route (the distance is zero) the merchant would be empty // Using the merchant is easier than explicitly checking for distance if (isMapDistanceRequest(transaction) && !getMerchant(transaction)) { - return translateLocal('iou.noRoute'); + // eslint-disable-next-line @typescript-eslint/no-deprecated + return translateLocal('violations.noRoute'); } if (isSentMoneyReportAction(reportAction)) { From 85e4f88ecf4bbbec2cfd20335781abaeb4392046 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Mon, 29 Dec 2025 10:11:02 +0100 Subject: [PATCH 05/25] exclude Null Island from valid waypoints --- src/libs/TransactionUtils/index.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index ae79013ef4003..2ddeafe35ee96 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -1526,6 +1526,10 @@ function waypointHasValidAddress(waypoint: RecentWaypoint | Waypoint): boolean { return !!waypoint?.address?.trim(); } +function isWaypointNullIsland(waypoint: RecentWaypoint | Waypoint): boolean { + return !waypoint.lat && !waypoint.lng; +} + /** * Converts the key of a waypoint to its index */ @@ -1564,6 +1568,11 @@ function getValidWaypoints(waypoints: WaypointCollection | undefined, reArrangeI return acc; } + // Exclude null island + if (!isWaypointNullIsland(currentWaypoint)) { + return acc; + } + // Check for adjacent waypoints with the same address or coordinate const previousCoordinate: Coordinate | undefined = previousWaypoint?.lng && previousWaypoint?.lat ? [previousWaypoint.lng, previousWaypoint.lat] : undefined; const currentCoordinate: Coordinate | undefined = currentWaypoint.lng && currentWaypoint.lat ? [currentWaypoint.lng, currentWaypoint.lat] : undefined; @@ -2505,6 +2514,7 @@ export { hasPendingUI, getWaypointIndex, waypointHasValidAddress, + isWaypointNullIsland, getRecentTransactions, hasReservationList, hasViolation, From f032cfe7327a7d47c65525779630c955c7e88ffe Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Mon, 29 Dec 2025 10:13:02 +0100 Subject: [PATCH 06/25] fetch route only if all waypoints are valid --- src/hooks/useFetchRoute.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/hooks/useFetchRoute.ts b/src/hooks/useFetchRoute.ts index b5aae9700906e..618fd53aae929 100644 --- a/src/hooks/useFetchRoute.ts +++ b/src/hooks/useFetchRoute.ts @@ -26,7 +26,12 @@ export default function useFetchRoute( const previousValidatedWaypoints = usePrevious(validatedWaypoints); const haveValidatedWaypointsChanged = !deepEqual(previousValidatedWaypoints, validatedWaypoints); const isMapDistanceRequest = isDistanceRequest(transaction) && !isManualDistanceRequest(transaction); - const shouldFetchRoute = isMapDistanceRequest && (isRouteAbsentWithoutErrors || haveValidatedWaypointsChanged) && !isLoadingRoute && Object.keys(validatedWaypoints).length > 1; + const shouldFetchRoute = + isMapDistanceRequest && + (isRouteAbsentWithoutErrors || haveValidatedWaypointsChanged) && + !isLoadingRoute && + Object.keys(waypoints ?? {}).length === Object.keys(validatedWaypoints).length && + Object.keys(validatedWaypoints).length > 1; useEffect(() => { if (isOffline || !shouldFetchRoute || !transaction?.transactionID) { From 3918ade5bf74f99cb57c0e4f59323c24f3b41e44 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Mon, 29 Dec 2025 10:14:59 +0100 Subject: [PATCH 07/25] fix condition --- src/libs/TransactionUtils/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 2ddeafe35ee96..1aba0127c0138 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -1569,7 +1569,7 @@ function getValidWaypoints(waypoints: WaypointCollection | undefined, reArrangeI } // Exclude null island - if (!isWaypointNullIsland(currentWaypoint)) { + if (isWaypointNullIsland(currentWaypoint)) { return acc; } From 5bd8064119db28ef3b6d4fe737c4dfd464ef3114 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Mon, 29 Dec 2025 10:32:34 +0100 Subject: [PATCH 08/25] display RBR in invalid waypoints --- src/components/DistanceRequest/DistanceRequestRenderItem.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/DistanceRequest/DistanceRequestRenderItem.tsx b/src/components/DistanceRequest/DistanceRequestRenderItem.tsx index 7364cd9adf4be..a1e882a014586 100644 --- a/src/components/DistanceRequest/DistanceRequestRenderItem.tsx +++ b/src/components/DistanceRequest/DistanceRequestRenderItem.tsx @@ -4,6 +4,8 @@ import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useTheme from '@hooks/useTheme'; +import {isWaypointNullIsland} from '@libs/TransactionUtils'; +import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import type {WaypointCollection} from '@src/types/onyx/Transaction'; @@ -54,6 +56,7 @@ function DistanceRequestRenderItem({waypoints, item = '', onSecondaryInteraction const waypoint = waypoints?.[`waypoint${index}`] ?? {}; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const title = waypoint.name || waypoint.address; + const errorText = isWaypointNullIsland(waypoint) ? translate('violations.noRoute') : undefined; return ( ); } From 44685ba0b8521bf9e7e8072a9382290a03dea8d7 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Mon, 29 Dec 2025 11:59:07 +0100 Subject: [PATCH 09/25] a waypoint is a null island only if it has explicit lat/lng = 0 --- src/libs/TransactionUtils/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index 1aba0127c0138..5dee31ef4d32e 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -1527,7 +1527,7 @@ function waypointHasValidAddress(waypoint: RecentWaypoint | Waypoint): boolean { } function isWaypointNullIsland(waypoint: RecentWaypoint | Waypoint): boolean { - return !waypoint.lat && !waypoint.lng; + return waypoint.lat === 0 && waypoint.lng === 0; } /** From 9d36ca440fc38913e4ac3827b1a6e8586053136f Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Mon, 29 Dec 2025 13:45:33 +0100 Subject: [PATCH 10/25] remove lat/lng zero-fallback to distinguish pending waypoint vs null island --- src/libs/actions/Transaction.ts | 26 ++++++++++++------- .../request/step/IOURequestStepWaypoint.tsx | 16 ++++++------ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 7160070eb243a..284fe5959d369 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -2,7 +2,7 @@ import {getUnixTime} from 'date-fns'; import {deepEqual} from 'fast-equals'; import lodashClone from 'lodash/clone'; import lodashHas from 'lodash/has'; -import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; +import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as API from '@libs/API'; import type {ChangeTransactionsReportParams, DismissViolationParams, GetRouteParams, MarkAsCashParams, TransactionThreadInfo} from '@libs/API/parameters'; @@ -117,10 +117,23 @@ type SaveWaypointProps = { }; function saveWaypoint({transactionID, index, waypoint, isDraft = false, recentWaypointsList = []}: SaveWaypointProps) { + // Saving a waypoint should completely overwrite the existing one at the given index (if any). + // Onyx merge performs noop on undefined fields. Thus we should fallback to null so the existing fields are cleared. + const waypointOnyxUpdate: Required> | null = waypoint + ? { + name: waypoint.name ?? null, + address: waypoint.address ?? null, + lat: waypoint.lat ?? null, + lng: waypoint.lng ?? null, + keyForList: waypoint.keyForList ?? null, + pendingAction: waypoint.pendingAction ?? null, + } + : null; + Onyx.merge(`${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, { comment: { waypoints: { - [`waypoint${index}`]: waypoint, + [`waypoint${index}`]: waypointOnyxUpdate, }, customUnit: { quantity: null, @@ -146,16 +159,9 @@ function saveWaypoint({transactionID, index, waypoint, isDraft = false, recentWa }, }); - // You can save offline waypoints without verifying the address (we will geocode it on the backend) - // We're going to prevent saving those addresses in the recent waypoints though since they could be invalid addresses - // However, in the backend once we verify the address, we will save the waypoint in the recent waypoints NVP - if (!lodashHas(waypoint, 'lat') || !lodashHas(waypoint, 'lng')) { - return; - } - // If current location is used, we would want to avoid saving it as a recent waypoint. This prevents the 'Your Location' // text from showing up in the address search suggestions - if (deepEqual(waypoint?.address, CONST.YOUR_LOCATION_TEXT)) { + if (waypoint?.address === CONST.YOUR_LOCATION_TEXT) { return; } const recentWaypointAlreadyExists = recentWaypointsList.find((recentWaypoint) => recentWaypoint?.address === waypoint?.address); diff --git a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx index 8dccabcca5386..24cb998777b55 100644 --- a/src/pages/iou/request/step/IOURequestStepWaypoint.tsx +++ b/src/pages/iou/request/step/IOURequestStepWaypoint.tsx @@ -140,10 +140,10 @@ function IOURequestStepWaypoint({ // Therefore, we're going to save the waypoint as just the address, and the lat/long will be filled in on the backend if (isOffline && waypointValue) { const waypoint = { - address: waypointValue ?? '', - name: values.name ?? '', - lat: values.lat ?? 0, - lng: values.lng ?? 0, + address: waypointValue, + name: values.name, + lat: values.lat, + lng: values.lng, keyForList: `${(values.name ?? 'waypoint') as string}_${Date.now()}`, }; save(waypoint); @@ -162,10 +162,10 @@ function IOURequestStepWaypoint({ const selectWaypoint = (values: Waypoint) => { const waypoint = { - lat: values.lat ?? 0, - lng: values.lng ?? 0, - address: values.address ?? '', - name: values.name ?? '', + lat: values.lat, + lng: values.lng, + address: values.address, + name: values.name, keyForList: `${values.name ?? 'waypoint'}_${Date.now()}`, }; From 694dd2a94bd7956892f54722177211c10e475f92 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 31 Dec 2025 09:21:55 +0100 Subject: [PATCH 11/25] update waypoints properly in Onyx.merge --- src/libs/actions/Transaction.ts | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts index 284fe5959d369..5b21ce3ea0dbc 100644 --- a/src/libs/actions/Transaction.ts +++ b/src/libs/actions/Transaction.ts @@ -47,7 +47,7 @@ import type { } from '@src/types/onyx'; import type {OriginalMessageIOU, OriginalMessageModifiedExpense} from '@src/types/onyx/OriginalMessage'; import type {OnyxData} from '@src/types/onyx/Request'; -import type {WaypointCollection} from '@src/types/onyx/Transaction'; +import type {Waypoint, WaypointCollection} from '@src/types/onyx/Transaction'; import type TransactionState from '@src/types/utils/TransactionStateType'; import {getPolicyTagsData} from './Policy/Tag'; import {getCurrentUserAccountID} from './Report'; @@ -360,9 +360,33 @@ function getRoute(transactionID: string, waypoints: WaypointCollection, routeTyp * which will replace the existing ones. */ function updateWaypoints(transactionID: string, waypoints: WaypointCollection, isDraft = false): Promise { + // Updating waypoints should completely overwrite the existing ones. + // Onyx merge performs noop on undefined fields. Thus we should fallback to null so the existing fields are cleared. + const waypointsOnyxUpdate = Object.keys(waypoints).reduce( + (acc, key) => { + const waypoint = waypoints[key]; + acc[key] = { + name: waypoint.name ?? null, + address: waypoint.address ?? null, + lat: waypoint.lat ?? null, + lng: waypoint.lng ?? null, + city: 'city' in waypoint ? (waypoint.city ?? null) : null, + state: 'state' in waypoint ? (waypoint.state ?? null) : null, + zipCode: 'zipCode' in waypoint ? (waypoint.zipCode ?? null) : null, + country: 'country' in waypoint ? (waypoint.country ?? null) : null, + street: 'street' in waypoint ? (waypoint.street ?? null) : null, + street2: 'street2' in waypoint ? (waypoint.street2 ?? null) : null, + pendingAction: 'pendingAction' in waypoint ? (waypoint.pendingAction ?? null) : null, + keyForList: waypoint.keyForList ?? null, + }; + return acc; + }, + {} as Record>>, + ); + return Onyx.merge(`${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, { comment: { - waypoints, + waypoints: waypointsOnyxUpdate, customUnit: { quantity: null, }, From 85fb8cf9180d8e591f0e38b79f76c29840c757fa Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 31 Dec 2025 09:46:59 +0100 Subject: [PATCH 12/25] revert useFetchRoute logic --- src/hooks/useFetchRoute.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/hooks/useFetchRoute.ts b/src/hooks/useFetchRoute.ts index 618fd53aae929..b5aae9700906e 100644 --- a/src/hooks/useFetchRoute.ts +++ b/src/hooks/useFetchRoute.ts @@ -26,12 +26,7 @@ export default function useFetchRoute( const previousValidatedWaypoints = usePrevious(validatedWaypoints); const haveValidatedWaypointsChanged = !deepEqual(previousValidatedWaypoints, validatedWaypoints); const isMapDistanceRequest = isDistanceRequest(transaction) && !isManualDistanceRequest(transaction); - const shouldFetchRoute = - isMapDistanceRequest && - (isRouteAbsentWithoutErrors || haveValidatedWaypointsChanged) && - !isLoadingRoute && - Object.keys(waypoints ?? {}).length === Object.keys(validatedWaypoints).length && - Object.keys(validatedWaypoints).length > 1; + const shouldFetchRoute = isMapDistanceRequest && (isRouteAbsentWithoutErrors || haveValidatedWaypointsChanged) && !isLoadingRoute && Object.keys(validatedWaypoints).length > 1; useEffect(() => { if (isOffline || !shouldFetchRoute || !transaction?.transactionID) { From 23bcbd946424b8c5f43fad4fac928adf14ad5d25 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:13:06 +0100 Subject: [PATCH 13/25] better check if distance is zero --- src/libs/ReportUtils.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index bbd6614d16427..33b318d113852 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -290,6 +290,7 @@ import { getCategory, getCurrency, getDescription, + getDistanceInMeters, getFormattedCreated, getFormattedPostedDate, getMCCGroup, @@ -5028,9 +5029,8 @@ function getTransactionReportName({ return translateLocal('iou.fieldPending'); } - // If a map distance request has no route (the distance is zero) the merchant would be empty - // Using the merchant is easier than explicitly checking for distance - if (isMapDistanceRequest(transaction) && !getMerchant(transaction)) { + // The unit does not matter as we are only interested in whether the distance is zero or not + if (isMapDistanceRequest(transaction) && !getDistanceInMeters(transaction, CONST.CUSTOM_UNITS.DISTANCE_UNIT_KILOMETERS)) { // eslint-disable-next-line @typescript-eslint/no-deprecated return translateLocal('violations.noRoute'); } From 2843062039383cd2042ac3c8b36c77f793f71a2a Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 7 Jan 2026 22:20:40 +0100 Subject: [PATCH 14/25] better error messages --- src/pages/iou/request/step/IOURequestStepDistance.tsx | 6 +++++- src/pages/iou/request/step/IOURequestStepDistanceMap.tsx | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index dbe40b54a93c1..11637e0ed13cc 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -52,7 +52,7 @@ import {getParticipantsOption, getReportOption} from '@libs/OptionsListUtils'; import {getPolicy, isPaidGroupPolicy} from '@libs/PolicyUtils'; import {getPolicyExpenseChat, isArchivedReport, isPolicyExpenseChat as isPolicyExpenseChatUtil} from '@libs/ReportUtils'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; -import {getDistanceInMeters, getRateID, getRequestType, getValidWaypoints, hasRoute, isCustomUnitRateIDForP2P} from '@libs/TransactionUtils'; +import {getDistanceInMeters, getRateID, getRequestType, getValidWaypoints, hasRoute, isCustomUnitRateIDForP2P, isWaypointNullIsland} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -140,6 +140,7 @@ function IOURequestStepDistance({ const nonEmptyWaypointsCount = useMemo(() => Object.keys(waypoints).filter((key) => !isWaypointEmpty(waypoints[key])).length, [waypoints]); const currentUserAccountIDParam = currentUserPersonalDetails.accountID; const currentUserEmailParam = currentUserPersonalDetails.login ?? ''; + const isWaypointsNullIslandError = useMemo(() => Object.values(waypoints).some(isWaypointNullIsland), [waypoints]); const duplicateWaypointsError = useMemo( () => nonEmptyWaypointsCount >= 2 && Object.keys(validatedWaypoints).length !== nonEmptyWaypointsCount, [nonEmptyWaypointsCount, validatedWaypoints], @@ -467,6 +468,9 @@ function IOURequestStepDistance({ if (hasRouteError) { return getLatestErrorField(transaction, 'route'); } + if (isWaypointsNullIslandError) { + return {isWaypointsNullIslandError: `${translate('common.please')} ${translate('common.fixTheErrors')} ${translate('common.inTheFormBeforeContinuing')}.`} as Errors; + } if (duplicateWaypointsError) { return {duplicateWaypointsError: translate('iou.error.duplicateWaypointsErrorMessage')} as Errors; } diff --git a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx index 14c6d780ae407..0ff4aba0b7ac4 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx @@ -52,7 +52,7 @@ import {getParticipantsOption, getReportOption} from '@libs/OptionsListUtils'; import {getPolicy, isPaidGroupPolicy} from '@libs/PolicyUtils'; import {getPolicyExpenseChat, isArchivedReport, isPolicyExpenseChat as isPolicyExpenseChatUtil} from '@libs/ReportUtils'; import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils'; -import {getDistanceInMeters, getRateID, getRequestType, getValidWaypoints, hasRoute, isCustomUnitRateIDForP2P} from '@libs/TransactionUtils'; +import {getDistanceInMeters, getRateID, getRequestType, getValidWaypoints, hasRoute, isCustomUnitRateIDForP2P, isWaypointNullIsland} from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; @@ -138,7 +138,7 @@ function IOURequestStepDistanceMap({ return isEmpty(waypointWithoutKey); }; const nonEmptyWaypointsCount = useMemo(() => Object.keys(waypoints).filter((key) => !isWaypointEmpty(waypoints[key])).length, [waypoints]); - + const isWaypointsNullIslandError = useMemo(() => Object.values(waypoints).some(isWaypointNullIsland), [waypoints]); const duplicateWaypointsError = useMemo( () => nonEmptyWaypointsCount >= 2 && Object.keys(validatedWaypoints).length !== nonEmptyWaypointsCount, [nonEmptyWaypointsCount, validatedWaypoints], @@ -469,6 +469,9 @@ function IOURequestStepDistanceMap({ if (hasRouteError) { return getLatestErrorField(transaction, 'route'); } + if (isWaypointsNullIslandError) { + return {isWaypointsNullIslandError: `${translate('common.please')} ${translate('common.fixTheErrors')} ${translate('common.inTheFormBeforeContinuing')}.`} as Errors; + } if (duplicateWaypointsError) { return {duplicateWaypointsError: translate('iou.error.duplicateWaypointsErrorMessage')} as Errors; } From 5106cc28eec6072543a619ab5910f7905ce22b77 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 7 Jan 2026 22:24:18 +0100 Subject: [PATCH 15/25] remove outdated test --- tests/unit/TransactionTest.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/tests/unit/TransactionTest.ts b/tests/unit/TransactionTest.ts index 15806e7c026b2..255d6f0a6483c 100644 --- a/tests/unit/TransactionTest.ts +++ b/tests/unit/TransactionTest.ts @@ -828,20 +828,6 @@ describe('Transaction', () => { expect(updatedRecentWaypoints?.[0]?.address).toBe('123 Main St'); }); - it('should not save waypoint if missing lat/lng', async () => { - const transactionID = 'txn2'; - const index = '1'; - const waypoint: RecentWaypoint = { - address: 'No LatLng', - }; - const recentWaypointsList: RecentWaypoint[] = []; - saveWaypoint({transactionID, index, waypoint, isDraft: false, recentWaypointsList}); - await waitForBatchedUpdates(); - - const updatedRecentWaypoints = await OnyxUtils.get(ONYXKEYS.NVP_RECENT_WAYPOINTS); - expect(updatedRecentWaypoints?.length ?? 0).toBe(0); - }); - it('should not save waypoint if address is YOUR_LOCATION_TEXT', async () => { const transactionID = 'txn3'; const index = '2'; From f479f8e547e4fad021a8ec6097574b0927545cde Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 7 Jan 2026 22:31:16 +0100 Subject: [PATCH 16/25] lint --- .../DistanceRequest/DistanceRequestRenderItem.tsx | 9 ++++----- src/libs/actions/Transaction.ts | 2 -- src/pages/iou/request/step/IOURequestStepWaypoint.tsx | 5 +++-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/components/DistanceRequest/DistanceRequestRenderItem.tsx b/src/components/DistanceRequest/DistanceRequestRenderItem.tsx index a1e882a014586..98870d7d928ae 100644 --- a/src/components/DistanceRequest/DistanceRequestRenderItem.tsx +++ b/src/components/DistanceRequest/DistanceRequestRenderItem.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import * as Expensicons from '@components/Icon/Expensicons'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; @@ -34,7 +33,7 @@ type DistanceRequestProps = { function DistanceRequestRenderItem({waypoints, item = '', onSecondaryInteraction, getIndex, isActive = false, onPress = () => {}, disabled = false}: DistanceRequestProps) { const theme = useTheme(); - const expensifyIcons = useMemoizedLazyExpensifyIcons(['Location']); + const expensifyIcons = useMemoizedLazyExpensifyIcons(['Location', 'DotIndicatorUnfilled', 'DotIndicator', 'DragHandles']); const {translate} = useLocalize(); const numberOfWaypoints = Object.keys(waypoints ?? {}).length; const lastWaypointIndex = numberOfWaypoints - 1; @@ -44,13 +43,13 @@ function DistanceRequestRenderItem({waypoints, item = '', onSecondaryInteraction let waypointIcon; if (index === 0) { descriptionKey += 'start'; - waypointIcon = Expensicons.DotIndicatorUnfilled; + waypointIcon = expensifyIcons.DotIndicatorUnfilled; } else if (index === lastWaypointIndex) { descriptionKey += 'stop'; waypointIcon = expensifyIcons.Location; } else { descriptionKey += 'stop'; - waypointIcon = Expensicons.DotIndicator; + waypointIcon = expensifyIcons.DotIndicator; } const waypoint = waypoints?.[`waypoint${index}`] ?? {}; @@ -62,7 +61,7 @@ function DistanceRequestRenderItem({waypoints, item = '', onSecondaryInteraction (); const navigation = useNavigation(); @@ -207,7 +208,7 @@ function IOURequestStepWaypoint({ shouldSetModalVisibility={false} threeDotsMenuItems={[ { - icon: Expensicons.Trashcan, + icon: icons.Trashcan, text: translate('distance.deleteWaypoint'), onSelected: () => { setRestoreFocusType(undefined); From 6c481fb23e0eca8ab3d0ff3cf5d39b6221fc970d Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Thu, 8 Jan 2026 22:48:32 +0100 Subject: [PATCH 17/25] optimistically clear noRoute violation --- src/libs/actions/IOU/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index d77a9c7ac05c0..f0e33f21ea64d 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -4299,7 +4299,8 @@ function getUpdateMoneyRequestParams(params: GetUpdateMoneyRequestParamsType): U isPaidGroupPolicy(policy) && !isInvoice && updatedTransaction && - (hasModifiedTag || + (hasPendingWaypoints || + hasModifiedTag || hasModifiedCategory || hasModifiedComment || hasModifiedMerchant || @@ -4321,6 +4322,9 @@ function getUpdateMoneyRequestParams(params: GetUpdateMoneyRequestParamsType): U hasModifiedCategory && transactionChanges.category === '' ? optimisticViolations.filter((violation) => violation.name !== CONST.VIOLATIONS.CATEGORY_OUT_OF_POLICY) : optimisticViolations; + if (hasPendingWaypoints) { + optimisticViolations = optimisticViolations.filter((violation) => violation.name !== CONST.VIOLATIONS.NO_ROUTE); + } const violationsOnyxData = ViolationsUtils.getViolationsOnyxData( updatedTransaction, From 5870028eed2f3bf293bb0aa08a547185a770fc41 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Thu, 8 Jan 2026 22:58:50 +0100 Subject: [PATCH 18/25] prevent submit if transaction has noRoute violation --- src/libs/ReportPreviewActionUtils.ts | 4 ++-- src/libs/ReportPrimaryActionUtils.ts | 4 ++-- src/libs/ReportSecondaryActionUtils.ts | 4 ++-- src/libs/TransactionUtils/index.ts | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/libs/ReportPreviewActionUtils.ts b/src/libs/ReportPreviewActionUtils.ts index 5199b2bcf7568..cacc0ba7623de 100644 --- a/src/libs/ReportPreviewActionUtils.ts +++ b/src/libs/ReportPreviewActionUtils.ts @@ -19,7 +19,7 @@ import { isReportApproved, isSettled, } from './ReportUtils'; -import {hasSmartScanFailedViolation, isPending, isScanning} from './TransactionUtils'; +import {hasSmartScanFailedOrNoRouteViolation, isPending, isScanning} from './TransactionUtils'; function canSubmit( report: Report, @@ -46,7 +46,7 @@ function canSubmit( const isAnyReceiptBeingScanned = transactions?.some((transaction) => isScanning(transaction)); - if (transactions?.some((transaction) => hasSmartScanFailedViolation(transaction, violations, currentUserEmail, currentUserAccountID, report, policy))) { + if (transactions?.some((transaction) => hasSmartScanFailedOrNoRouteViolation(transaction, violations, currentUserEmail, currentUserAccountID, report, policy))) { return false; } diff --git a/src/libs/ReportPrimaryActionUtils.ts b/src/libs/ReportPrimaryActionUtils.ts index f1bbd576cdbfe..5af8092e58e6c 100644 --- a/src/libs/ReportPrimaryActionUtils.ts +++ b/src/libs/ReportPrimaryActionUtils.ts @@ -41,7 +41,7 @@ import { allHavePendingRTERViolation, getTransactionViolations, hasPendingRTERViolation as hasPendingRTERViolationTransactionUtils, - hasSmartScanFailedViolation, + hasSmartScanFailedOrNoRouteViolation, isDuplicate, isOnHold as isOnHoldTransactionUtils, isPending, @@ -113,7 +113,7 @@ function isSubmitAction( } if (violations && currentUserEmail && currentUserAccountID !== undefined) { - if (reportTransactions.some((transaction) => hasSmartScanFailedViolation(transaction, violations, currentUserEmail, currentUserAccountID, report, policy))) { + if (reportTransactions.some((transaction) => hasSmartScanFailedOrNoRouteViolation(transaction, violations, currentUserEmail, currentUserAccountID, report, policy))) { return false; } } diff --git a/src/libs/ReportSecondaryActionUtils.ts b/src/libs/ReportSecondaryActionUtils.ts index 9be38b2abadcf..88f6d5f9cc2e0 100644 --- a/src/libs/ReportSecondaryActionUtils.ts +++ b/src/libs/ReportSecondaryActionUtils.ts @@ -67,7 +67,7 @@ import { allHavePendingRTERViolation, getOriginalTransactionWithSplitInfo, hasReceipt as hasReceiptTransactionUtils, - hasSmartScanFailedViolation, + hasSmartScanFailedOrNoRouteViolation, isDistanceRequest as isDistanceRequestTransactionUtils, isDuplicate, isManagedCardTransaction as isManagedCardTransactionTransactionUtils, @@ -189,7 +189,7 @@ function isSubmitAction({ } if (violations && currentUserEmail && currentUserAccountID !== undefined) { - if (reportTransactions.some((transaction) => hasSmartScanFailedViolation(transaction, violations, currentUserEmail, currentUserAccountID, report, policy))) { + if (reportTransactions.some((transaction) => hasSmartScanFailedOrNoRouteViolation(transaction, violations, currentUserEmail, currentUserAccountID, report, policy))) { return false; } } diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts index cdd02b22855c7..f4dbc4fb62aa3 100644 --- a/src/libs/TransactionUtils/index.ts +++ b/src/libs/TransactionUtils/index.ts @@ -2470,9 +2470,9 @@ function isExpenseUnreported(transaction?: Transaction): transaction is Unreport } /** - * Check if there is a smartscan failed violation for the transaction. + * Check if there is a smartscan failed or no route violation for the transaction. */ -function hasSmartScanFailedViolation( +function hasSmartScanFailedOrNoRouteViolation( transaction: Transaction, transactionViolations: OnyxCollection | undefined, currentUserEmail: string, @@ -2481,7 +2481,7 @@ function hasSmartScanFailedViolation( policy: OnyxEntry, ): boolean { const violations = getTransactionViolations(transaction, transactionViolations, currentUserEmail, currentUserAccountID, report, policy); - return !!violations?.some((violation) => violation.name === CONST.VIOLATIONS.SMARTSCAN_FAILED); + return !!violations?.some((violation) => violation.name === CONST.VIOLATIONS.SMARTSCAN_FAILED || violation.name === CONST.VIOLATIONS.NO_ROUTE); } /** @@ -2586,7 +2586,7 @@ export { hasViolation, hasDuplicateTransactions, hasBrokenConnectionViolation, - hasSmartScanFailedViolation, + hasSmartScanFailedOrNoRouteViolation, shouldShowBrokenConnectionViolation, shouldShowBrokenConnectionViolationForMultipleTransactions, hasNoticeTypeViolation, From 0c931635a848e722c2cd442c22added1398c7258 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Thu, 8 Jan 2026 23:15:16 +0100 Subject: [PATCH 19/25] add spanish translation --- src/languages/es.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/languages/es.ts b/src/languages/es.ts index 1063019bf1bc2..4f0c793c1aa11 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -7358,6 +7358,7 @@ ${amount} para ${merchant} - ${date}`, hold: 'Este gasto está retenido', resolvedDuplicates: 'resolvió el duplicado', companyCardRequired: 'Se requieren compras con la tarjeta de la empresa.', + noRoute: 'Por favor, selecciona una dirección válida.', }, reportViolations: { [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: ({fieldName}) => `${fieldName} es obligatorio`, From 2f973db150962117ea36c40d483834529650df3e Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Thu, 8 Jan 2026 23:16:47 +0100 Subject: [PATCH 20/25] update translation --- src/languages/es.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/es.ts b/src/languages/es.ts index 4f0c793c1aa11..573cfd8653220 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -7358,7 +7358,7 @@ ${amount} para ${merchant} - ${date}`, hold: 'Este gasto está retenido', resolvedDuplicates: 'resolvió el duplicado', companyCardRequired: 'Se requieren compras con la tarjeta de la empresa.', - noRoute: 'Por favor, selecciona una dirección válida.', + noRoute: 'Por favor, selecciona una dirección válida', }, reportViolations: { [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: ({fieldName}) => `${fieldName} es obligatorio`, From db21ee7e271e342df07890a06ad611e79f83899c Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Thu, 8 Jan 2026 23:39:34 +0100 Subject: [PATCH 21/25] pass categories and tags for proper violations calculation --- src/libs/actions/IOU/index.ts | 12 ++++++------ .../iou/request/step/IOURequestStepDistance.tsx | 4 ++++ .../request/step/IOURequestStepDistanceManual.tsx | 4 ++++ .../iou/request/step/IOURequestStepDistanceMap.tsx | 4 ++++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts index f0e33f21ea64d..b0b9419d74461 100644 --- a/src/libs/actions/IOU/index.ts +++ b/src/libs/actions/IOU/index.ts @@ -4947,9 +4947,9 @@ type UpdateMoneyRequestDistanceParams = { waypoints?: WaypointCollection; distance?: number; routes?: Routes; - policy?: OnyxEntry; - policyTagList?: OnyxEntry; - policyCategories?: OnyxEntry; + policy: OnyxEntry; + policyTagList: OnyxEntry; + policyCategories: OnyxEntry; transactionBackup: OnyxEntry; currentUserAccountIDParam: number; currentUserEmailParam: string; @@ -4963,9 +4963,9 @@ function updateMoneyRequestDistance({ waypoints, distance, routes = undefined, - policy = {} as OnyxTypes.Policy, - policyTagList = {}, - policyCategories = {}, + policy, + policyTagList, + policyCategories, transactionBackup, currentUserAccountIDParam, currentUserEmailParam, diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index d1e833c7d5b3f..6d582278389c2 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -88,6 +88,8 @@ function IOURequestStepDistance({ const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`, {canBeMissing: true}); const [transactionBackup] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_BACKUP}${transactionID}`, {canBeMissing: true}); const policy = usePolicy(report?.policyID); + const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policy?.id}`, {canBeMissing: true}); + const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policy?.id}`, {canBeMissing: true}); const personalPolicy = usePersonalPolicy(); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); const defaultExpensePolicy = useDefaultExpensePolicy(); @@ -538,6 +540,8 @@ function IOURequestStepDistance({ waypoints, ...(hasRouteChanged ? {routes: transaction?.routes} : {}), policy, + policyTagList: policyTags, + policyCategories, transactionBackup, currentUserAccountIDParam, currentUserEmailParam, diff --git a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx index 0d94ade068558..5c76e5cbb3849 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx @@ -81,6 +81,8 @@ function IOURequestStepDistanceManual({ const [selectedTab, selectedTabResult] = useOnyx(`${ONYXKEYS.COLLECTION.SELECTED_TAB}${CONST.TAB.DISTANCE_REQUEST_TYPE}`, {canBeMissing: true}); const isLoadingSelectedTab = isLoadingOnyxValue(selectedTabResult); const policy = usePolicy(report?.policyID); + const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policy?.id}`, {canBeMissing: true}); + const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policy?.id}`, {canBeMissing: true}); const personalPolicy = usePersonalPolicy(); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); const defaultExpensePolicy = useDefaultExpensePolicy(); @@ -177,6 +179,8 @@ function IOURequestStepDistanceManual({ // Not required for manual distance request transactionBackup: undefined, policy, + policyTagList: policyTags, + policyCategories, currentUserAccountIDParam, currentUserEmailParam, isASAPSubmitBetaEnabled, diff --git a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx index 8db13f4a6a033..ce20386a6d86b 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx @@ -89,6 +89,8 @@ function IOURequestStepDistanceMap({ const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID}`, {canBeMissing: true}); const [transactionBackup] = useOnyx(`${ONYXKEYS.COLLECTION.TRANSACTION_BACKUP}${transactionID}`, {canBeMissing: true}); const policy = usePolicy(report?.policyID); + const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policy?.id}`, {canBeMissing: true}); + const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policy?.id}`, {canBeMissing: true}); const personalPolicy = usePersonalPolicy(); const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {canBeMissing: false}); const defaultExpensePolicy = useDefaultExpensePolicy(); @@ -539,6 +541,8 @@ function IOURequestStepDistanceMap({ waypoints, ...(hasRouteChanged ? {routes: transaction?.routes} : {}), policy, + policyTagList: policyTags, + policyCategories, transactionBackup, currentUserAccountIDParam, currentUserEmailParam, From 1a160d87e02d597f9d6d09a9806330eb118e2ee3 Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Thu, 8 Jan 2026 23:46:09 +0100 Subject: [PATCH 22/25] lint --- src/pages/iou/request/step/IOURequestStepDistance.tsx | 3 +++ src/pages/iou/request/step/IOURequestStepDistanceManual.tsx | 3 +++ src/pages/iou/request/step/IOURequestStepDistanceMap.tsx | 2 ++ 3 files changed, 8 insertions(+) diff --git a/src/pages/iou/request/step/IOURequestStepDistance.tsx b/src/pages/iou/request/step/IOURequestStepDistance.tsx index 6d582278389c2..e28807995f7a1 100644 --- a/src/pages/iou/request/step/IOURequestStepDistance.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistance.tsx @@ -463,6 +463,7 @@ function IOURequestStepDistance({ quickAction, introSelected, activePolicyID, + policyRecentlyUsedCurrencies, ]); const getError = () => { @@ -570,6 +571,8 @@ function IOURequestStepDistance({ transaction?.routes, report?.reportID, policy, + policyTags, + policyCategories, currentUserAccountIDParam, currentUserEmailParam, isASAPSubmitBetaEnabled, diff --git a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx index 5c76e5cbb3849..e92a3f032a16c 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceManual.tsx @@ -314,6 +314,8 @@ function IOURequestStepDistanceManual({ transaction, reportID, policy, + policyTags, + policyCategories, shouldSkipConfirmation, personalDetails, reportAttributesDerived, @@ -331,6 +333,7 @@ function IOURequestStepDistanceManual({ quickAction, introSelected, activePolicyID, + policyRecentlyUsedCurrencies, ], ); diff --git a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx index ce20386a6d86b..88409c885e13d 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceMap.tsx @@ -571,6 +571,8 @@ function IOURequestStepDistanceMap({ transaction?.routes, report?.reportID, policy, + policyTags, + policyCategories, currentUserAccountIDParam, currentUserEmailParam, isASAPSubmitBetaEnabled, From 7c3b293f5e997d144e4973483e0b92ca886d41ee Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Wed, 14 Jan 2026 19:50:32 +0100 Subject: [PATCH 23/25] translations --- src/languages/de.ts | 30 +++++++++++++++--------------- src/languages/fr.ts | 28 ++++++++++++++-------------- src/languages/it.ts | 26 +++++++++++++------------- src/languages/ja.ts | 24 ++++++++++++------------ src/languages/nl.ts | 30 +++++++++++++++--------------- src/languages/pl.ts | 24 +++++++++++++----------- src/languages/pt-BR.ts | 20 ++++++++++---------- src/languages/zh-hans.ts | 25 +++++++++++++------------ 8 files changed, 105 insertions(+), 102 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index ea5cae055bba5..17f0bea9b3b54 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -255,7 +255,6 @@ const translations: TranslationDeepObject = { searchWithThreeDots: 'Suchen …', next: 'Weiter', previous: 'Zurück', - // @context Navigation button that returns the user to the previous screen. Should be interpreted as a UI action label. goBack: 'Zurück', create: 'Erstellen', add: 'Hinzufügen', @@ -715,7 +714,7 @@ const translations: TranslationDeepObject = { noExtensionFoundForMimeType: 'Keine Erweiterung für diesen MIME-Typ gefunden', problemGettingImageYouPasted: 'Es gab ein Problem beim Abrufen des von dir eingefügten Bildes', commentExceededMaxLength: (formattedMaxLength: string) => `Die maximale Kommentarlänge beträgt ${formattedMaxLength} Zeichen.`, - taskTitleExceededMaxLength: (formattedMaxLength: string) => `Die maximale Aufgabenüberschrift darf ${formattedMaxLength} Zeichen lang sein.`, + taskTitleExceededMaxLength: (formattedMaxLength: string) => `Die maximale Länge des Aufgabentitels beträgt ${formattedMaxLength} Zeichen.`, }, baseUpdateAppModal: { updateApp: 'App aktualisieren', @@ -973,9 +972,9 @@ const translations: TranslationDeepObject = { chooseSpreadsheetMultiLevelTag: `Wähle eine Tabellenkalkulationsdatei zum Importieren aus. Erfahre mehr über unterstützte Dateiformate.`, fileContainsHeader: 'Datei enthält Spaltenüberschriften', column: (name: string) => `Spalte ${name}`, - fieldNotMapped: (fieldName: string) => `Ups! Ein erforderliches Feld („${fieldName}“) wurde nicht zugeordnet. Bitte überprüfen und erneut versuchen.`, - singleFieldMultipleColumns: (fieldName: string) => `Ups! Du hast ein einzelnes Feld („${fieldName}“) mehreren Spalten zugeordnet. Bitte überprüfe dies und versuche es erneut.`, - emptyMappedField: (fieldName: string) => `Ups! Das Feld („${fieldName}“) enthält einen oder mehrere leere Werte. Bitte überprüfen Sie es und versuchen Sie es erneut.`, + fieldNotMapped: (fieldName: string) => `Ups! Ein erforderliches Feld („${fieldName}“) wurde nicht zugeordnet. Bitte überprüfe es und versuche es erneut.`, + singleFieldMultipleColumns: (fieldName: string) => `Hoppla! Du hast ein einzelnes Feld („${fieldName}“) mehreren Spalten zugeordnet. Bitte überprüfe dies und versuche es erneut.`, + emptyMappedField: (fieldName: string) => `Ups! Das Feld („${fieldName}“) enthält einen oder mehrere leere Werte. Bitte überprüfen und erneut versuchen.`, importSuccessfulTitle: 'Import erfolgreich', importCategoriesSuccessfulDescription: (categories: number) => (categories > 1 ? `${categories} Kategorien wurden hinzugefügt.` : '1 Kategorie wurde hinzugefügt.'), importMembersSuccessfulDescription: (added: number, updated: number) => { @@ -992,7 +991,7 @@ const translations: TranslationDeepObject = { }, importTagsSuccessfulDescription: (tags: number) => (tags > 1 ? `${tags} Tags wurden hinzugefügt.` : '1 Tag wurde hinzugefügt.'), importMultiLevelTagsSuccessfulDescription: 'Hierarchische Tags wurden hinzugefügt.', - importPerDiemRatesSuccessfulDescription: (rates: number) => (rates > 1 ? `${rates} Übernachtungspauschalen wurden hinzugefügt.` : '1 Pauschale wurde hinzugefügt.'), + importPerDiemRatesSuccessfulDescription: (rates: number) => (rates > 1 ? `${rates} Tagegelder wurden hinzugefügt.` : '1 Pauschale wurde hinzugefügt.'), importFailedTitle: 'Import fehlgeschlagen', importFailedDescription: 'Bitte stelle sicher, dass alle Felder korrekt ausgefüllt sind, und versuche es erneut. Wenn das Problem weiterhin besteht, wende dich bitte an Concierge.', importDescription: 'Wählen Sie aus, welche Felder aus Ihrer Tabelle zugeordnet werden sollen, indem Sie auf das Dropdown-Menü neben jeder der importierten Spalten unten klicken.', @@ -1113,7 +1112,7 @@ const translations: TranslationDeepObject = { movedFromReport: ({reportName}: MovedFromReportParams) => `hat eine Ausgabe verschoben${reportName ? `von ${reportName}` : ''}`, movedTransactionTo: ({reportUrl, reportName}: MovedTransactionParams) => `hat diese Ausgabe verschoben${reportName ? `zu ${reportName}` : ''}`, movedTransactionFrom: ({reportUrl, reportName}: MovedTransactionParams) => `hat diese Ausgabe verschoben${reportName ? `von ${reportName}` : ''}`, - movedUnreportedTransaction: ({reportUrl}: MovedTransactionParams) => `hat diese Ausgabe aus deinem Persönlichen Bereich verschoben`, + movedUnreportedTransaction: ({reportUrl}: MovedTransactionParams) => `hat diese Ausgabe aus deinem persönlichen Bereich verschoben`, unreportedTransaction: ({reportUrl}: MovedTransactionParams) => `hat diese Ausgabe in deinen persönlichen Bereich verschoben`, movedAction: ({shouldHideMovedReportUrl, movedReportUrl, newParentReportUrl, toPolicyName}: MovedActionParams) => { if (shouldHideMovedReportUrl) { @@ -2320,7 +2319,7 @@ ${amount} für ${merchant} – ${date}`, transferAmountPage: { transfer: ({amount}: TransferParams) => `Überweisen${amount ? ` ${amount}` : ''}`, instant: 'Sofort (Debitkarte)', - instantSummary: (rate: string, minAmount: string) => `${rate}% Gebühr (mindestens ${minAmount})`, + instantSummary: (rate: string, minAmount: string) => `${rate}% Gebühr (${minAmount} Minimum)`, ach: '1–3 Werktage (Bankkonto)', achSummary: 'Keine Gebühr', whichAccount: 'Welches Konto?', @@ -2855,7 +2854,7 @@ ${ dateShouldBeBefore: (dateString: string) => `Das Datum sollte vor dem ${dateString} liegen`, dateShouldBeAfter: (dateString: string) => `Datum muss nach ${dateString} liegen`, hasInvalidCharacter: 'Name darf nur lateinische Zeichen enthalten', - incorrectZipFormat: (zipFormat?: string) => `Ungültiges Postleitzahlformat${zipFormat ? `Akzeptables Format: ${zipFormat}` : ''}`, + incorrectZipFormat: (zipFormat?: string) => `Falsches Postleitzahlformat${zipFormat ? `Zulässiges Format: ${zipFormat}` : ''}`, invalidPhoneNumber: `Bitte stelle sicher, dass die Telefonnummer gültig ist (z. B. ${CONST.EXAMPLE_PHONE_NUMBER})`, }, }, @@ -3612,7 +3611,7 @@ ${ flight: 'Flug', flightDetails: { passenger: 'Passagier', - layover: (layover: string) => `Sie haben einen ${layover} Zwischenstopp vor diesem Flug`, + layover: (layover: string) => `Du hast einen ${layover} Aufenthalt vor diesem Flug`, takeOff: 'Abflug', landing: 'Startseite', seat: 'Sitz', @@ -4646,19 +4645,19 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: { label: 'Standard-NetSuite-Mitarbeiter', description: 'Nicht in Expensify importiert, beim Export angewendet', - footerContent: (importField: string) => - `Wenn du ${importField} in NetSuite verwendest, wenden wir den Standardwert an, der im Mitarbeitendendatensatz festgelegt ist, sobald nach „Expense Report“ oder „Journal Entry“ exportiert wird.`, + footerContent: (importField: string) => `${startCase(importField)}-Auswahl wird auf alle Ausgaben in dem Bericht eines Mitarbeiters angewendet.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG]: { label: 'Stichwörter', description: 'Auf Positionsebene', - footerContent: (importField: string) => `${startCase(importField)} wird für jede einzelne Ausgabe im Bericht eines Mitarbeiters auswählbar sein.`, + footerContent: (importField: string) => `${startCase(importField)}-Auswahl wird auf alle Ausgaben in dem Bericht eines Mitarbeiters angewendet.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: { label: 'Berichtsfelder', description: 'Berichtsebene', - footerContent: (importField: string) => `${startCase(importField)}-Auswahl gilt für alle Ausgaben auf dem Bericht eines Mitarbeiters.`, + footerContent: (importField: string) => `${startCase(importField)}-Auswahl wird auf alle Ausgaben in dem Bericht eines Mitarbeiters angewendet.`, }, + footerContent: (importField: string) => `${startCase(importField)}-Auswahl wird auf alle Ausgaben in dem Bericht eines Mitarbeiters angewendet.`, }, }, }, @@ -4735,7 +4734,7 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU commercialFeedPlaidDetails: `Erfordert die Einrichtung mit Ihrer Bank, aber wir führen Sie durch den Prozess. Dies ist in der Regel auf größere Unternehmen beschränkt.`, directFeedDetails: 'Der einfachste Ansatz. Verbinde dich direkt mit deinen Master-Zugangsdaten. Diese Methode ist am gebräuchlichsten.', enableFeed: { - title: (provider: string) => `Aktiviere deinen ${provider}-Feed`, + title: (provider: string) => `Aktivieren Sie Ihren ${provider}-Feed`, heading: 'Wir verfügen über eine direkte Integration mit Ihrem Kartenaussteller und können Ihre Transaktionsdaten schnell und genau in Expensify importieren.\n\nUm zu beginnen, gehen Sie einfach wie folgt vor:', visa: 'Wir verfügen über globale Integrationen mit Visa, wobei die Berechtigung je nach Bank und Kartenprogramm variiert.\n\nUm loszulegen, gehen Sie einfach wie folgt vor:', @@ -7274,6 +7273,7 @@ Fordere Spesendetails wie Belege und Beschreibungen an, lege Limits und Standard hold: 'Diese Ausgabe wurde zurückgestellt', resolvedDuplicates: 'Duplikat behoben', companyCardRequired: 'Firmenkartenkäufe erforderlich', + noRoute: 'Bitte wählen Sie eine gültige Adresse aus', }, reportViolations: { [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: ({fieldName}: RequiredFieldParams) => `${fieldName} ist erforderlich`, diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 028256e8173bb..930cf93458661 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -255,7 +255,6 @@ const translations: TranslationDeepObject = { searchWithThreeDots: 'Rechercher...', next: 'Suivant', previous: 'Précédent', - // @context Navigation button that returns the user to the previous screen. Should be interpreted as a UI action label. goBack: 'Retour', create: 'Créer', add: 'Ajouter', @@ -716,7 +715,7 @@ const translations: TranslationDeepObject = { composer: { noExtensionFoundForMimeType: 'Aucune extension trouvée pour le type MIME', problemGettingImageYouPasted: 'Un problème est survenu lors de la récupération de l’image que vous avez collée', - commentExceededMaxLength: (formattedMaxLength: string) => `La longueur maximale du commentaire est de ${formattedMaxLength} caractères.`, + commentExceededMaxLength: (formattedMaxLength: string) => `La longueur maximale d’un commentaire est de ${formattedMaxLength} caractères.`, taskTitleExceededMaxLength: (formattedMaxLength: string) => `La longueur maximale du titre de la tâche est de ${formattedMaxLength} caractères.`, }, baseUpdateAppModal: { @@ -974,9 +973,9 @@ const translations: TranslationDeepObject = { chooseSpreadsheetMultiLevelTag: `Sélectionnez un fichier de feuille de calcul à importer. En savoir plus sur les formats de fichier pris en charge.`, fileContainsHeader: 'Le fichier contient des en-têtes de colonnes', column: (name: string) => `Colonne ${name}`, - fieldNotMapped: (fieldName: string) => `Oups ! Un champ requis (« ${fieldName} ») n’a pas été associé. Veuillez vérifier et réessayer.`, - singleFieldMultipleColumns: (fieldName: string) => `Oups ! Vous avez associé un seul champ (« ${fieldName} ») à plusieurs colonnes. Veuillez vérifier et réessayer.`, - emptyMappedField: (fieldName: string) => `Oups ! Le champ (« ${fieldName} ») contient une ou plusieurs valeurs vides. Veuillez vérifier et réessayer.`, + fieldNotMapped: (fieldName: string) => `Oups ! Un champ requis (« ${fieldName} ») n’a pas été associé. Veuillez vérifier et réessayer.`, + singleFieldMultipleColumns: (fieldName: string) => `Oups ! Vous avez associé un seul champ (« ${fieldName} ») à plusieurs colonnes. Veuillez vérifier et réessayer.`, + emptyMappedField: (fieldName: string) => `Oups ! Le champ (« ${fieldName} ») contient une ou plusieurs valeurs vides. Veuillez vérifier et réessayer.`, importSuccessfulTitle: 'Importation réussie', importCategoriesSuccessfulDescription: (categories: number) => (categories > 1 ? `${categories} catégories ont été ajoutées.` : '1 catégorie a été ajoutée.'), importMembersSuccessfulDescription: (added: number, updated: number) => { @@ -991,9 +990,9 @@ const translations: TranslationDeepObject = { } return added > 1 ? `${added} membres ont été ajoutés.` : '1 membre a été ajouté.'; }, - importTagsSuccessfulDescription: (tags: number) => (tags > 1 ? `${tags} libellés ont été ajoutés.` : '1 tag a été ajouté.'), + importTagsSuccessfulDescription: (tags: number) => (tags > 1 ? `${tags} libellés ont été ajoutés.` : '1 étiquette a été ajoutée.'), importMultiLevelTagsSuccessfulDescription: 'Des balises à plusieurs niveaux ont été ajoutées.', - importPerDiemRatesSuccessfulDescription: (rates: number) => (rates > 1 ? `Les taux de per diem ${rates} ont été ajoutés.` : '1 taux de per diem a été ajouté.'), + importPerDiemRatesSuccessfulDescription: (rates: number) => (rates > 1 ? `${rates} taux de per diem ont été ajoutés.` : '1 indemnité journalière a été ajoutée.'), importFailedTitle: 'Échec de l’importation', importFailedDescription: 'Veuillez vous assurer que tous les champs sont correctement remplis, puis réessayez. Si le problème persiste, veuillez contacter Concierge.', importDescription: 'Choisissez quels champs mapper à partir de votre feuille de calcul en cliquant sur le menu déroulant à côté de chaque colonne importée ci-dessous.', @@ -2863,7 +2862,7 @@ ${ dateShouldBeBefore: (dateString: string) => `La date doit être antérieure au ${dateString}`, dateShouldBeAfter: (dateString: string) => `La date doit être postérieure au ${dateString}`, hasInvalidCharacter: 'Le nom ne peut inclure que des caractères latins', - incorrectZipFormat: (zipFormat?: string) => `Format de code postal incorrect${zipFormat ? `Format acceptable : ${zipFormat}` : ''}`, + incorrectZipFormat: (zipFormat?: string) => `Format de code postal incorrect${zipFormat ? `Format acceptable : ${zipFormat}` : ''}`, invalidPhoneNumber: `Veuillez vous assurer que le numéro de téléphone est valide (p. ex. ${CONST.EXAMPLE_PHONE_NUMBER})`, }, }, @@ -3723,7 +3722,7 @@ ${ flightScheduleChangePending: (airlineCode: string) => `La compagnie aérienne a proposé un changement d’horaire pour le vol ${airlineCode} ; nous attendons la confirmation.`, flightScheduleChangeClosed: (airlineCode: string, startDate?: string) => `Changement d’horaire confirmé : le vol ${airlineCode} part désormais à ${startDate}.`, flightUpdated: (airlineCode: string, origin: string, destination: string, startDate: string) => - `Votre vol ${airlineCode} (${origin} → ${destination}) le ${startDate} a été mis à jour.`, + `Votre vol ${airlineCode} (${origin} → ${destination}) du ${startDate} a été mis à jour.`, flightCabinChanged: (airlineCode: string, cabinClass?: string) => `Votre classe de cabine a été mise à jour en ${cabinClass} sur le vol ${airlineCode}.`, flightSeatConfirmed: (airlineCode: string) => `Votre siège attribué sur le vol ${airlineCode} a été confirmé.`, flightSeatChanged: (airlineCode: string) => `Votre siège attribué sur le vol ${airlineCode} a été modifié.`, @@ -4654,19 +4653,19 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST. [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: { label: 'Paramètre par défaut de l’employé NetSuite', description: 'Non importé dans Expensify, appliqué à l’exportation', - footerContent: (importField: string) => - `Si vous utilisez ${importField} dans NetSuite, nous appliquerons la valeur par défaut définie sur la fiche employé lors de l’exportation vers le rapport de dépenses ou l’écriture de journal.`, + footerContent: (importField: string) => `La sélection ${startCase(importField)} s’appliquera à toutes les dépenses du rapport d’un employé.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG]: { label: 'Étiquettes', description: 'Niveau poste de ligne', - footerContent: (importField: string) => `${startCase(importField)} sera sélectionnable pour chaque dépense individuelle dans le rapport d'un employé.`, + footerContent: (importField: string) => `La sélection ${startCase(importField)} s’appliquera à toutes les dépenses du rapport d’un employé.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: { label: 'Champs du rapport', description: 'Niveau de rapport', - footerContent: (importField: string) => `La sélection ${startCase(importField)} s’appliquera à toutes les dépenses sur le rapport d’un employé.`, + footerContent: (importField: string) => `La sélection ${startCase(importField)} s’appliquera à toutes les dépenses du rapport d’un employé.`, }, + footerContent: (importField: string) => `La sélection ${startCase(importField)} s’appliquera à toutes les dépenses du rapport d’un employé.`, }, }, }, @@ -4743,7 +4742,7 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST. commercialFeedPlaidDetails: `Nécessite une configuration avec votre banque, mais nous vous guiderons. Cela est généralement réservé aux grandes entreprises.`, directFeedDetails: 'L’approche la plus simple. Connectez-vous immédiatement à l’aide de vos identifiants principaux. Cette méthode est la plus courante.', enableFeed: { - title: (provider: string) => `Activer votre flux ${provider}`, + title: (provider: string) => `Activez votre flux ${provider}`, heading: 'Nous avons une intégration directe avec l’émetteur de votre carte et pouvons importer rapidement et avec précision vos données de transactions dans Expensify.\n\nPour commencer, il vous suffit de :', visa: 'Nous disposons d’intégrations globales avec Visa, bien que l’éligibilité varie selon la banque et le programme de carte.\n\nPour commencer, il vous suffit de :', @@ -7287,6 +7286,7 @@ Exigez des informations de dépense comme les reçus et les descriptions, défin hold: 'Cette dépense a été mise en attente', resolvedDuplicates: 'a résolu le doublon', companyCardRequired: 'Achats avec carte d’entreprise requis', + noRoute: 'Veuillez sélectionner une adresse valide', }, reportViolations: { [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: ({fieldName}: RequiredFieldParams) => `${fieldName} est requis`, diff --git a/src/languages/it.ts b/src/languages/it.ts index 69fe7fae1e22a..316e50de68041 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -255,8 +255,7 @@ const translations: TranslationDeepObject = { searchWithThreeDots: 'Cerca...', next: 'Avanti', previous: 'Precedente', - // @context Navigation button that returns the user to the previous screen. Should be interpreted as a UI action label. - goBack: 'Torna indietro', + goBack: 'Indietro', create: 'Crea', add: 'Aggiungi', resend: 'Reinvia', @@ -685,7 +684,7 @@ const translations: TranslationDeepObject = { protectedPDFNotSupported: 'I PDF protetti da password non sono supportati', attachmentImageResized: 'Questa immagine è stata ridimensionata per l’anteprima. Scarica per la risoluzione completa.', attachmentImageTooLarge: 'Questa immagine è troppo grande per essere visualizzata in anteprima prima del caricamento.', - tooManyFiles: (fileLimit: number) => `Puoi caricare al massimo ${fileLimit} file alla volta.`, + tooManyFiles: (fileLimit: number) => `Puoi caricare solo fino a ${fileLimit} file alla volta.`, sizeExceededWithValue: ({maxUploadSizeInMB}: SizeExceededParams) => `I file superano ${maxUploadSizeInMB} MB. Riprova.`, someFilesCantBeUploaded: 'Alcuni file non possono essere caricati', sizeLimitExceeded: ({maxUploadSizeInMB}: SizeExceededParams) => `I file devono essere inferiori a ${maxUploadSizeInMB} MB. I file più grandi non verranno caricati.`, @@ -971,11 +970,11 @@ const translations: TranslationDeepObject = { chooseSpreadsheetMultiLevelTag: `Seleziona un file di foglio di calcolo da importare. Ulteriori informazioni sui formati di file supportati.`, fileContainsHeader: 'Il file contiene intestazioni di colonna', column: (name: string) => `Colonna ${name}`, - fieldNotMapped: (fieldName: string) => `Ops! Un campo obbligatorio ("${fieldName}") non è stato mappato. Controlla e riprova.`, + fieldNotMapped: (fieldName: string) => `Ops! Un campo obbligatorio ("${fieldName}") non è stato mappato. Per favore, controlla e riprova.`, singleFieldMultipleColumns: (fieldName: string) => `Ops! Hai associato un singolo campo ("${fieldName}") a più colonne. Controlla e riprova.`, - emptyMappedField: (fieldName: string) => `Ops! Il campo («${fieldName}») contiene uno o più valori vuoti. Controlla e riprova.`, + emptyMappedField: (fieldName: string) => `Ops! Il campo ("${fieldName}") contiene uno o più valori vuoti. Controlla e riprova.`, importSuccessfulTitle: 'Importazione riuscita', - importCategoriesSuccessfulDescription: (categories: number) => (categories > 1 ? `Sono state aggiunte ${categories} categorie.` : '1 categoria è stata aggiunta.'), + importCategoriesSuccessfulDescription: (categories: number) => (categories > 1 ? `Sono state aggiunte ${categories} categorie.` : 'È stata aggiunta 1 categoria.'), importMembersSuccessfulDescription: (added: number, updated: number) => { if (!added && !updated) { return 'Nessun membro è stato aggiunto o aggiornato.'; @@ -990,7 +989,7 @@ const translations: TranslationDeepObject = { }, importTagsSuccessfulDescription: (tags: number) => (tags > 1 ? `Sono stati aggiunti ${tags} tag.` : 'È stato aggiunto 1 tag.'), importMultiLevelTagsSuccessfulDescription: 'Sono stati aggiunti i tag multilivello.', - importPerDiemRatesSuccessfulDescription: (rates: number) => (rates > 1 ? `Sono state aggiunte le tariffe di diaria ${rates}.` : 'È stata aggiunta 1 tariffa di diaria.'), + importPerDiemRatesSuccessfulDescription: (rates: number) => (rates > 1 ? `Le tariffe di diaria ${rates} sono state aggiunte.` : 'È stata aggiunta 1 indennità giornaliera.'), importFailedTitle: 'Importazione non riuscita', importFailedDescription: 'Assicurati che tutti i campi siano compilati correttamente e riprova. Se il problema persiste, contatta Concierge.', importDescription: 'Scegli quali campi mappare dal tuo foglio di calcolo cliccando il menu a discesa accanto a ciascuna colonna importata qui sotto.', @@ -2317,7 +2316,7 @@ ${amount} per ${merchant} - ${date}`, transferAmountPage: { transfer: ({amount}: TransferParams) => `Trasferimento${amount ? ` ${amount}` : ''}`, instant: 'Immediato (Carta di debito)', - instantSummary: (rate: string, minAmount: string) => `Commissione del ${rate}% (${minAmount} minimo)`, + instantSummary: (rate: string, minAmount: string) => `Commissione ${rate}% (${minAmount} minimo)`, ach: '1-3 giorni lavorativi (Conto bancario)', achSummary: 'Nessuna commissione', whichAccount: 'Quale conto?', @@ -2849,7 +2848,7 @@ ${ dateShouldBeBefore: (dateString: string) => `La data deve essere precedente a ${dateString}`, dateShouldBeAfter: (dateString: string) => `La data deve essere successiva a ${dateString}`, hasInvalidCharacter: 'Il nome può includere solo caratteri latini', - incorrectZipFormat: (zipFormat?: string) => `Formato del codice postale non corretto${zipFormat ? `Formato accettabile: ${zipFormat}` : ''}`, + incorrectZipFormat: (zipFormat?: string) => `Formato codice postale non corretto${zipFormat ? `Formato accettabile: ${zipFormat}` : ''}`, invalidPhoneNumber: `Assicurati che il numero di telefono sia valido (ad es. ${CONST.EXAMPLE_PHONE_NUMBER})`, }, }, @@ -4635,19 +4634,19 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST. [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: { label: 'Impostazione predefinita dipendente NetSuite', description: "Non importato in Expensify, applicato all'esportazione", - footerContent: (importField: string) => - `Se utilizzi ${importField} in NetSuite, applicheremo il valore predefinito impostato nel record del dipendente all'esportazione verso Expense Report o Journal Entry.`, + footerContent: (importField: string) => `${startCase(importField)} la selezione verrà applicata a tutte le spese nel report di un dipendente.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG]: { label: 'Tag', description: 'A livello di singola voce', - footerContent: (importField: string) => `${startCase(importField)} sarà selezionabile per ogni singola spesa nel report di un dipendente.`, + footerContent: (importField: string) => `${startCase(importField)} la selezione verrà applicata a tutte le spese nel report di un dipendente.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: { label: 'Campi del report', description: 'Livello del report', - footerContent: (importField: string) => `La selezione ${startCase(importField)} verrà applicata a tutte le spese nel rendiconto di un dipendente.`, + footerContent: (importField: string) => `${startCase(importField)} la selezione verrà applicata a tutte le spese nel report di un dipendente.`, }, + footerContent: (importField: string) => `${startCase(importField)} la selezione verrà applicata a tutte le spese nel report di un dipendente.`, }, }, }, @@ -7262,6 +7261,7 @@ Richiedi dettagli di spesa come ricevute e descrizioni, imposta limiti e valori hold: 'Questa spesa è stata messa in sospeso', resolvedDuplicates: 'ha risolto il duplicato', companyCardRequired: 'Acquisti con carta aziendale obbligatori', + noRoute: 'Seleziona un indirizzo valido', }, reportViolations: { [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: ({fieldName}: RequiredFieldParams) => `${fieldName} è obbligatorio`, diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 2e1c564d43fdc..0e74d605774a7 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -255,7 +255,6 @@ const translations: TranslationDeepObject = { searchWithThreeDots: '検索...', next: '次へ', previous: '前へ', - // @context Navigation button that returns the user to the previous screen. Should be interpreted as a UI action label. goBack: '戻る', create: '作成', add: '追加', @@ -684,12 +683,12 @@ const translations: TranslationDeepObject = { protectedPDFNotSupported: 'パスワードで保護されたPDFはサポートされていません', attachmentImageResized: 'この画像はプレビュー用にサイズが変更されています。フル解像度で表示するにはダウンロードしてください。', attachmentImageTooLarge: 'この画像はアップロード前にプレビューするには大きすぎます。', - tooManyFiles: (fileLimit: number) => `一度にアップロードできるファイルは${fileLimit}件までです。`, + tooManyFiles: (fileLimit: number) => `一度にアップロードできるファイルは最大 ${fileLimit} 件までです。`, sizeExceededWithValue: ({maxUploadSizeInMB}: SizeExceededParams) => `ファイルが ${maxUploadSizeInMB} MB を超えています。もう一度お試しください。`, someFilesCantBeUploaded: '一部のファイルはアップロードできません', sizeLimitExceeded: ({maxUploadSizeInMB}: SizeExceededParams) => `ファイルは${maxUploadSizeInMB}MB未満である必要があります。これより大きいファイルはアップロードされません。`, maxFileLimitExceeded: '一度にアップロードできる領収書は最大30枚です。それを超えた分はアップロードされません。', - unsupportedFileType: (fileType: string) => `${fileType} ファイルはサポートされていません。サポートされているファイル形式のみアップロードされます。`, + unsupportedFileType: (fileType: string) => `${fileType} ファイルには対応していません。対応しているファイルタイプのみアップロードされます。`, learnMoreAboutSupportedFiles: 'サポートされている形式の詳細はこちらをご覧ください。', passwordProtected: 'パスワード保護されたPDFはサポートされていません。サポートされているファイルのみがアップロードされます。', }, @@ -971,10 +970,10 @@ const translations: TranslationDeepObject = { fileContainsHeader: 'ファイルには列ヘッダーが含まれています', column: (name: string) => `列 ${name}`, fieldNotMapped: (fieldName: string) => `おっと!必須フィールド(「${fieldName}」)がマッピングされていません。確認して、もう一度お試しください。`, - singleFieldMultipleColumns: (fieldName: string) => `おっと!1 つのフィールド(「${fieldName}」)が複数の列に割り当てられています。内容を確認して、もう一度お試しください。`, + singleFieldMultipleColumns: (fieldName: string) => `おっと!1 つのフィールド(「${fieldName}」)が複数の列に割り当てられています。確認して、もう一度お試しください。`, emptyMappedField: (fieldName: string) => `おっと!フィールド(「${fieldName}」)に 1 つ以上の空の値が含まれています。確認して、もう一度お試しください。`, importSuccessfulTitle: 'インポートに成功しました', - importCategoriesSuccessfulDescription: (categories: number) => (categories > 1 ? `${categories}件のカテゴリーが追加されました。` : 'カテゴリを1件追加しました。'), + importCategoriesSuccessfulDescription: (categories: number) => (categories > 1 ? `${categories} 個のカテゴリーが追加されました。` : '1件のカテゴリが追加されました。'), importMembersSuccessfulDescription: (added: number, updated: number) => { if (!added && !updated) { return 'メンバーは追加または更新されていません。'; @@ -987,9 +986,9 @@ const translations: TranslationDeepObject = { } return added > 1 ? `${added} 名のメンバーが追加されました。` : '1名のメンバーが追加されました。'; }, - importTagsSuccessfulDescription: (tags: number) => (tags > 1 ? `${tags} 個のタグを追加しました。` : 'タグが 1 件追加されました。'), + importTagsSuccessfulDescription: (tags: number) => (tags > 1 ? `${tags} 個のタグを追加しました。` : 'タグが1件追加されました。'), importMultiLevelTagsSuccessfulDescription: 'マルチレベルタグが追加されました。', - importPerDiemRatesSuccessfulDescription: (rates: number) => (rates > 1 ? `${rates}件の日当レートが追加されました。` : '1件の日当レートが追加されました。'), + importPerDiemRatesSuccessfulDescription: (rates: number) => (rates > 1 ? `${rates} 件の日当レートが追加されました。` : '1件の日当レートが追加されました。'), importFailedTitle: 'インポートに失敗しました', importFailedDescription: 'すべての項目が正しく入力されていることを確認して、もう一度お試しください。問題が解決しない場合は、Concierge までお問い合わせください。', importDescription: '下のインポートされた各列の横にあるドロップダウンをクリックして、スプレッドシートからマッピングするフィールドを選択してください。', @@ -1232,7 +1231,7 @@ const translations: TranslationDeepObject = { `${submitterDisplayName} が30日以内に Expensify Wallet を有効化しなかったため、${amount} の支払いはキャンセルされました`, settledAfterAddedBankAccount: ({submitterDisplayName, amount}: SettledAfterAddedBankAccountParams) => `${submitterDisplayName} が銀行口座を追加しました。${amount} の支払いが行われました。`, - paidElsewhere: (payer?: string) => `${payer ? `${payer} ` : ''}は支払済みにマークされました`, + paidElsewhere: (payer?: string) => `${payer ? `${payer} ` : ''}支払い済みにマークしました`, paidWithExpensify: (payer?: string) => `${payer ? `${payer} ` : ''}はウォレットで支払い済み`, automaticallyPaidWithExpensify: (payer?: string) => `${payer ? `${payer} ` : ''}はワークスペースルールを通じてExpensifyで支払われました`, @@ -4609,19 +4608,19 @@ _より詳しい手順については、[ヘルプサイトをご覧ください [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: { label: 'NetSuite 従業員のデフォルト', description: 'Expensify にインポートされず、エクスポート時に適用されます', - footerContent: (importField: string) => - `NetSuite で ${importField} を使用する場合、Expense Report または Journal Entry へエクスポートする際に、従業員レコードに設定されているデフォルトを適用します。`, + footerContent: (importField: string) => `${startCase(importField)} の選択は、従業員のレポート上のすべての経費に適用されます。`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG]: { label: 'タグ', description: '明細行レベル', - footerContent: (importField: string) => `${startCase(importField)} は、従業員のレポート上の各経費ごとに個別に選択できるようになります。`, + footerContent: (importField: string) => `${startCase(importField)} の選択は、従業員のレポート上のすべての経費に適用されます。`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: { label: 'レポート項目', description: 'レポートレベル', footerContent: (importField: string) => `${startCase(importField)} の選択は、従業員のレポート上のすべての経費に適用されます。`, }, + footerContent: (importField: string) => `${startCase(importField)} の選択は、従業員のレポート上のすべての経費に適用されます。`, }, }, }, @@ -6673,7 +6672,7 @@ ${reportName} amount: { lessThan: ({amount}: OptionalParam = {}) => `${amount ?? ''} 未満`, greaterThan: ({amount}: OptionalParam = {}) => `${amount ?? ''}より大きい`, - between: (greaterThan: string, lessThan: string) => `${greaterThan} から ${lessThan} まで`, + between: (greaterThan: string, lessThan: string) => `${greaterThan} と ${lessThan} の間`, equalTo: ({amount}: OptionalParam = {}) => `${amount ?? ''} と等しい`, }, card: { @@ -7205,6 +7204,7 @@ ${reportName} hold: 'この経費は保留になっています', resolvedDuplicates: '重複を解決しました', companyCardRequired: '法人カードでの購入が必須', + noRoute: '有効な住所を選択してください', }, reportViolations: { [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: ({fieldName}: RequiredFieldParams) => `${fieldName} は必須です`, diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 80cabe25a54f4..0b917cdeedcc9 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -255,7 +255,6 @@ const translations: TranslationDeepObject = { searchWithThreeDots: 'Zoeken...', next: 'Volgende', previous: 'Vorige', - // @context Navigation button that returns the user to the previous screen. Should be interpreted as a UI action label. goBack: 'Ga terug', create: 'Maken', add: 'Toevoegen', @@ -715,8 +714,8 @@ const translations: TranslationDeepObject = { composer: { noExtensionFoundForMimeType: 'Geen extensie gevonden voor mimetype', problemGettingImageYouPasted: 'Er is een probleem opgetreden bij het ophalen van de afbeelding die je hebt geplakt', - commentExceededMaxLength: (formattedMaxLength: string) => `De maximale lengte van opmerkingen is ${formattedMaxLength} tekens.`, - taskTitleExceededMaxLength: (formattedMaxLength: string) => `De maximale lengte van de taaknaam is ${formattedMaxLength} tekens.`, + commentExceededMaxLength: (formattedMaxLength: string) => `De maximale lengte van een opmerking is ${formattedMaxLength} tekens.`, + taskTitleExceededMaxLength: (formattedMaxLength: string) => `De maximale lengte van een taaknaam is ${formattedMaxLength} tekens.`, }, baseUpdateAppModal: { updateApp: 'App bijwerken', @@ -971,11 +970,11 @@ const translations: TranslationDeepObject = { chooseSpreadsheetMultiLevelTag: `Selecteer een spreadsheetbestand om te importeren. Meer informatie over ondersteunde bestandsindelingen.`, fileContainsHeader: 'Bestand bevat kolomkoppen', column: (name: string) => `Kolom ${name}`, - fieldNotMapped: (fieldName: string) => `Oeps! Een verplicht veld ("${fieldName}") is niet toegewezen. Controleer dit en probeer het opnieuw.`, - singleFieldMultipleColumns: (fieldName: string) => `Oeps! Je hebt één veld ("${fieldName}") aan meerdere kolommen gekoppeld. Controleer dit en probeer het opnieuw.`, - emptyMappedField: (fieldName: string) => `Oeps! Het veld („${fieldName}”) bevat een of meer lege waarden. Controleer het en probeer het opnieuw.`, + fieldNotMapped: (fieldName: string) => `Oeps! Een verplicht veld (“${fieldName}”) is niet toegewezen. Controleer het en probeer het opnieuw.`, + singleFieldMultipleColumns: (fieldName: string) => `Oeps! Je hebt één veld (‘${fieldName}’) toegewezen aan meerdere kolommen. Controleer dit en probeer het opnieuw.`, + emptyMappedField: (fieldName: string) => `Oeps! Het veld ("${fieldName}") bevat een of meer lege waarden. Controleer het en probeer het opnieuw.`, importSuccessfulTitle: 'Import succesvol', - importCategoriesSuccessfulDescription: (categories: number) => (categories > 1 ? `Er zijn ${categories} categorieën toegevoegd.` : '1 categorie is toegevoegd.'), + importCategoriesSuccessfulDescription: (categories: number) => (categories > 1 ? `${categories} categorieën zijn toegevoegd.` : '1 categorie is toegevoegd.'), importMembersSuccessfulDescription: (added: number, updated: number) => { if (!added && !updated) { return 'Er zijn geen leden toegevoegd of bijgewerkt.'; @@ -988,7 +987,7 @@ const translations: TranslationDeepObject = { } return added > 1 ? `${added} leden zijn toegevoegd.` : '1 lid is toegevoegd.'; }, - importTagsSuccessfulDescription: (tags: number) => (tags > 1 ? `Er zijn ${tags} tags toegevoegd.` : '1 tag is toegevoegd.'), + importTagsSuccessfulDescription: (tags: number) => (tags > 1 ? `${tags} tags zijn toegevoegd.` : '1 tag is toegevoegd.'), importMultiLevelTagsSuccessfulDescription: 'Tags op meerdere niveaus zijn toegevoegd.', importPerDiemRatesSuccessfulDescription: (rates: number) => (rates > 1 ? `${rates} dagvergoedingen zijn toegevoegd.` : '1 dagvergoedingstarief is toegevoegd.'), importFailedTitle: 'Import mislukt', @@ -2314,7 +2313,7 @@ ${amount} voor ${merchant} - ${date}`, transferAmountPage: { transfer: ({amount}: TransferParams) => `Overboeking${amount ? ` ${amount}` : ''}`, instant: 'Direct (debitkaart)', - instantSummary: (rate: string, minAmount: string) => `${rate}% vergoeding (${minAmount} minimum)`, + instantSummary: (rate: string, minAmount: string) => `${rate}% kosten (${minAmount} minimum)`, ach: '1-3 werkdagen (bankrekening)', achSummary: 'Geen kosten', whichAccount: 'Welk account?', @@ -2846,7 +2845,7 @@ ${ dateShouldBeBefore: (dateString: string) => `Datum moet vóór ${dateString} zijn`, dateShouldBeAfter: (dateString: string) => `Datum moet na ${dateString} zijn`, hasInvalidCharacter: 'Naam mag alleen Latijnse tekens bevatten', - incorrectZipFormat: (zipFormat?: string) => `Ongeldig postcodeformaat${zipFormat ? `Acceptabel formaat: ${zipFormat}` : ''}`, + incorrectZipFormat: (zipFormat?: string) => `Onjuist formaat postcode${zipFormat ? `Acceptabel formaat: ${zipFormat}` : ''}`, invalidPhoneNumber: `Zorg ervoor dat het telefoonnummer geldig is (bijv. ${CONST.EXAMPLE_PHONE_NUMBER})`, }, }, @@ -3600,7 +3599,7 @@ ${ flight: 'Vlucht', flightDetails: { passenger: 'Passagier', - layover: (layover: string) => `Je hebt een ${layover} tussenstop vóór deze vlucht`, + layover: (layover: string) => `Je hebt een ${layover} tussenstop voor deze vlucht`, takeOff: 'Vertrek', landing: 'Landingspagina', seat: 'Licentie', @@ -4630,19 +4629,19 @@ _Voor gedetailleerdere instructies, [bezoek onze helpsite](${CONST.NETSUITE_IMPO [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: { label: 'Standaard NetSuite-medewerker', description: 'Niet in Expensify geïmporteerd, toegepast bij export', - footerContent: (importField: string) => - `Als je ${importField} in NetSuite gebruikt, passen we de standaardwaarde die is ingesteld op de werknemerskaart toe bij het exporteren naar Expense Report of Journal Entry.`, + footerContent: (importField: string) => `${startCase(importField)}-selectie wordt toegepast op alle uitgaven op het rapport van een werknemer.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG]: { label: 'Labels', description: 'Niveau van regelitems', - footerContent: (importField: string) => `${startCase(importField)} zal selecteerbaar zijn voor elke afzonderlijke onkost in het rapport van een werknemer.`, + footerContent: (importField: string) => `${startCase(importField)}-selectie wordt toegepast op alle uitgaven op het rapport van een werknemer.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: { label: 'Rapportvelden', description: 'Rapportniveau', - footerContent: (importField: string) => `${startCase(importField)}-selectie is van toepassing op alle onkosten op het rapport van een werknemer.`, + footerContent: (importField: string) => `${startCase(importField)}-selectie wordt toegepast op alle uitgaven op het rapport van een werknemer.`, }, + footerContent: (importField: string) => `${startCase(importField)}-selectie wordt toegepast op alle uitgaven op het rapport van een werknemer.`, }, }, }, @@ -7248,6 +7247,7 @@ Vraag verplichte uitgavedetails zoals bonnetjes en beschrijvingen, stel limieten hold: 'Deze uitgave is in de wacht gezet', resolvedDuplicates: 'het duplicaat opgelost', companyCardRequired: 'Aankopen met bedrijfskaart verplicht', + noRoute: 'Selecteer een geldig adres', }, reportViolations: { [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: ({fieldName}: RequiredFieldParams) => `${fieldName} is verplicht`, diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 02ea237dcdab4..d24d6213a756a 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -255,7 +255,6 @@ const translations: TranslationDeepObject = { searchWithThreeDots: 'Szukaj...', next: 'Dalej', previous: 'Wstecz', - // @context Navigation button that returns the user to the previous screen. Should be interpreted as a UI action label. goBack: 'Wróć', create: 'Utwórz', add: 'Dodaj', @@ -690,7 +689,7 @@ const translations: TranslationDeepObject = { someFilesCantBeUploaded: 'Niektórych plików nie można przesłać', sizeLimitExceeded: ({maxUploadSizeInMB}: SizeExceededParams) => `Pliki muszą mieć mniej niż ${maxUploadSizeInMB} MB. Większe pliki nie zostaną przesłane.`, maxFileLimitExceeded: 'Możesz jednorazowo przesłać maksymalnie 30 paragonów. Dodatkowe nie zostaną przesłane.', - unsupportedFileType: (fileType: string) => `Pliki ${fileType} nie są obsługiwane. Przesłane zostaną tylko obsługiwane typy plików.`, + unsupportedFileType: (fileType: string) => `Pliki typu ${fileType} nie są obsługiwane. Zostaną przesłane tylko obsługiwane typy plików.`, learnMoreAboutSupportedFiles: 'Dowiedz się więcej o obsługiwanych formatach.', passwordProtected: 'Pliki PDF zabezpieczone hasłem nie są obsługiwane. Zostaną przesłane tylko obsługiwane pliki.', }, @@ -971,7 +970,7 @@ const translations: TranslationDeepObject = { chooseSpreadsheetMultiLevelTag: `Wybierz plik arkusza kalkulacyjnego do zaimportowania. Dowiedz się więcej o obsługiwanych formatach plików.`, fileContainsHeader: 'Plik zawiera nagłówki kolumn', column: (name: string) => `Kolumna ${name}`, - fieldNotMapped: (fieldName: string) => `Ups! Wymagane pole („${fieldName}”) nie zostało zmapowane. Sprawdź wszystko i spróbuj ponownie.`, + fieldNotMapped: (fieldName: string) => `Ups! Wymagane pole („${fieldName}”) nie zostało zmapowane. Sprawdź i spróbuj ponownie.`, singleFieldMultipleColumns: (fieldName: string) => `Ups! Przypisałeś jedno pole („${fieldName}”) do wielu kolumn. Sprawdź ustawienia i spróbuj ponownie.`, emptyMappedField: (fieldName: string) => `Ups! Pole („${fieldName}”) zawiera jedną lub więcej pustych wartości. Sprawdź je i spróbuj ponownie.`, importSuccessfulTitle: 'Import zakończony pomyślnie', @@ -988,9 +987,9 @@ const translations: TranslationDeepObject = { } return added > 1 ? `Dodano ${added} członków.` : 'Dodano 1 członka.'; }, - importTagsSuccessfulDescription: (tags: number) => (tags > 1 ? `Dodano ${tags} tagów.` : 'Dodano 1 tag.'), + importTagsSuccessfulDescription: (tags: number) => (tags > 1 ? `Dodano tagi: ${tags}.` : 'Dodano 1 tag.'), importMultiLevelTagsSuccessfulDescription: 'Dodano tagi wielopoziomowe.', - importPerDiemRatesSuccessfulDescription: (rates: number) => (rates > 1 ? `Dodano ${rates} stawek ryczałtowych.` : 'Dodano 1 stawkę diety.'), + importPerDiemRatesSuccessfulDescription: (rates: number) => (rates > 1 ? `Dodano stawki diet w wysokości ${rates}.` : 'Dodano 1 stawkę diety.'), importFailedTitle: 'Import zakończony niepowodzeniem', importFailedDescription: 'Upewnij się, że wszystkie pola zostały wypełnione poprawnie i spróbuj ponownie. Jeśli problem będzie się powtarzał, skontaktuj się z Concierge.', importDescription: 'Wybierz pola do zmapowania z arkusza kalkulacyjnego, klikając menu rozwijane obok każdej zaimportowanej kolumny poniżej.', @@ -3697,7 +3696,9 @@ ${ flightScheduleChangePending: (airlineCode: string) => `Linie lotnicze zaproponowały zmianę rozkładu lotu ${airlineCode}; oczekujemy na potwierdzenie.`, flightScheduleChangeClosed: (airlineCode: string, startDate?: string) => `Zmiana planu potwierdzona: lot ${airlineCode} odlot teraz o ${startDate}.`, flightUpdated: (airlineCode: string, origin: string, destination: string, startDate: string) => - `Twój lot ${airlineCode} (${origin} → ${destination}) w dniu ${startDate} został zaktualizowany.`, + `Twój lot ${airlineCode} (${origin} → ${ + destination //_/\__/_/ \_,_/\__/\__/\_,_/ + }) w dniu ${startDate} został zaktualizowany.`, flightCabinChanged: (airlineCode: string, cabinClass?: string) => `Twoja klasa kabiny została zaktualizowana do ${cabinClass} na locie ${airlineCode}.`, flightSeatConfirmed: (airlineCode: string) => `Twoje miejsce na locie ${airlineCode} zostało potwierdzone.`, flightSeatChanged: (airlineCode: string) => `Twoje miejsce na locie ${airlineCode} zostało zmienione.`, @@ -4622,19 +4623,19 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: { label: 'Domyślne ustawienia pracownika NetSuite', description: 'Niezaimportowane do Expensify, zastosowane przy eksporcie', - footerContent: (importField: string) => - `Jeśli używasz ${importField} w NetSuite, zastosujemy domyślną wartość ustawioną w rekordzie pracownika podczas eksportu do Raportu Wydatków lub Zapisu w Dzienniku.`, + footerContent: (importField: string) => `Wybrana opcja ${startCase(importField)} zostanie zastosowana do wszystkich wydatków w raporcie pracownika.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG]: { label: 'Tagi', description: 'Na poziomie pozycji', - footerContent: (importField: string) => `${startCase(importField)} będzie można wybrać osobno dla każdego wydatku w raporcie pracownika.`, + footerContent: (importField: string) => `Wybrana opcja ${startCase(importField)} zostanie zastosowana do wszystkich wydatków w raporcie pracownika.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: { label: 'Pola raportu', description: 'Poziom raportu', - footerContent: (importField: string) => `Wybór ${startCase(importField)} zostanie zastosowany do wszystkich wydatków w raporcie pracownika.`, + footerContent: (importField: string) => `Wybrana opcja ${startCase(importField)} zostanie zastosowana do wszystkich wydatków w raporcie pracownika.`, }, + footerContent: (importField: string) => `Wybrana opcja ${startCase(importField)} zostanie zastosowana do wszystkich wydatków w raporcie pracownika.`, }, }, }, @@ -6704,7 +6705,7 @@ Wymagaj szczegółów wydatków, takich jak paragony i opisy, ustawiaj limity i amount: { lessThan: ({amount}: OptionalParam = {}) => `Mniej niż ${amount ?? ''}`, greaterThan: ({amount}: OptionalParam = {}) => `Większe niż ${amount ?? ''}`, - between: (greaterThan: string, lessThan: string) => `Pomiędzy ${greaterThan} a ${lessThan}`, + between: (greaterThan: string, lessThan: string) => `Między ${greaterThan} a ${lessThan}`, equalTo: ({amount}: OptionalParam = {}) => `Równe ${amount ?? ''}`, }, card: { @@ -7237,6 +7238,7 @@ Wymagaj szczegółów wydatków, takich jak paragony i opisy, ustawiaj limity i hold: 'Ten wydatek został wstrzymany', resolvedDuplicates: 'rozwiązano duplikat', companyCardRequired: 'Wymagane zakupy kartą firmową', + noRoute: 'Wybierz prawidłowy adres', }, reportViolations: { [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: ({fieldName}: RequiredFieldParams) => `Pole ${fieldName} jest wymagane`, diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 77d1ace49f7e5..0036f8a4b7270 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -255,7 +255,6 @@ const translations: TranslationDeepObject = { searchWithThreeDots: 'Pesquisar...', next: 'Próximo', previous: 'Anterior', - // @context Navigation button that returns the user to the previous screen. Should be interpreted as a UI action label. goBack: 'Voltar', create: 'Criar', add: 'Adicionar', @@ -689,7 +688,7 @@ const translations: TranslationDeepObject = { someFilesCantBeUploaded: 'Alguns arquivos não podem ser enviados', sizeLimitExceeded: ({maxUploadSizeInMB}: SizeExceededParams) => `Os arquivos devem ter menos de ${maxUploadSizeInMB} MB. Arquivos maiores não serão carregados.`, maxFileLimitExceeded: 'Você pode enviar até 30 recibos por vez. Quaisquer extras não serão enviados.', - unsupportedFileType: (fileType: string) => `Arquivos ${fileType} não são compatíveis. Somente tipos de arquivo compatíveis serão enviados.`, + unsupportedFileType: (fileType: string) => `Arquivos ${fileType} não são compatíveis. Apenas os tipos de arquivo compatíveis serão enviados.`, learnMoreAboutSupportedFiles: 'Saiba mais sobre os formatos compatíveis.', passwordProtected: 'PDFs protegidos por senha não são compatíveis. Somente arquivos compatíveis serão enviados.', }, @@ -971,8 +970,8 @@ const translations: TranslationDeepObject = { fileContainsHeader: 'Arquivo contém cabeçalhos de coluna', column: (name: string) => `Coluna ${name}`, fieldNotMapped: (fieldName: string) => `Ops! Um campo obrigatório ("${fieldName}") não foi mapeado. Revise e tente novamente.`, - singleFieldMultipleColumns: (fieldName: string) => `Opa! Você associou um único campo ("${fieldName}") a várias colunas. Revise e tente novamente.`, - emptyMappedField: (fieldName: string) => `Opa! O campo ("${fieldName}") contém um ou mais valores vazios. Verifique e tente novamente.`, + singleFieldMultipleColumns: (fieldName: string) => `Ops! Você mapeou um único campo ("${fieldName}") para várias colunas. Revise e tente novamente.`, + emptyMappedField: (fieldName: string) => `Ops! O campo ("${fieldName}") contém um ou mais valores vazios. Revise e tente novamente.`, importSuccessfulTitle: 'Importação bem-sucedida', importCategoriesSuccessfulDescription: (categories: number) => (categories > 1 ? `${categories} categorias foram adicionadas.` : '1 categoria foi adicionada.'), importMembersSuccessfulDescription: (added: number, updated: number) => { @@ -989,7 +988,7 @@ const translations: TranslationDeepObject = { }, importTagsSuccessfulDescription: (tags: number) => (tags > 1 ? `${tags} tags foram adicionadas.` : '1 tag foi adicionada.'), importMultiLevelTagsSuccessfulDescription: 'Tags em vários níveis foram adicionadas.', - importPerDiemRatesSuccessfulDescription: (rates: number) => (rates > 1 ? `Foram adicionadas taxas diárias de ${rates}.` : '1 taxa de diária foi adicionada.'), + importPerDiemRatesSuccessfulDescription: (rates: number) => (rates > 1 ? `${rates} valores de diária foram adicionados.` : '1 taxa de diária foi adicionada.'), importFailedTitle: 'Falha na importação', importFailedDescription: 'Verifique se todos os campos foram preenchidos corretamente e tente novamente. Se o problema persistir, entre em contato com a Concierge.', importDescription: 'Escolha quais campos mapear da sua planilha clicando no menu suspenso ao lado de cada coluna importada abaixo.', @@ -4621,19 +4620,19 @@ _Para instruções mais detalhadas, [visite nosso site de ajuda](${CONST.NETSUIT [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: { label: 'Padrão de funcionário NetSuite', description: 'Não importado para o Expensify, aplicado na exportação', - footerContent: (importField: string) => - `Se você usar ${importField} no NetSuite, aplicaremos o padrão definido no registro do funcionário ao exportar para Expense Report ou Journal Entry.`, + footerContent: (importField: string) => `A seleção ${startCase(importField)} será aplicada a todas as despesas no relatório de um funcionário.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG]: { label: 'Tags', description: 'Nível de item de linha', - footerContent: (importField: string) => `${startCase(importField)} estará disponível para seleção em cada despesa individual no relatório de um funcionário.`, + footerContent: (importField: string) => `A seleção ${startCase(importField)} será aplicada a todas as despesas no relatório de um funcionário.`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: { label: 'Campos de relatório', description: 'Nível de relatório', - footerContent: (importField: string) => `A seleção de ${startCase(importField)} será aplicada a todas as despesas no relatório de um funcionário.`, + footerContent: (importField: string) => `A seleção ${startCase(importField)} será aplicada a todas as despesas no relatório de um funcionário.`, }, + footerContent: (importField: string) => `A seleção ${startCase(importField)} será aplicada a todas as despesas no relatório de um funcionário.`, }, }, }, @@ -4709,7 +4708,7 @@ _Para instruções mais detalhadas, [visite nosso site de ajuda](${CONST.NETSUIT commercialFeedPlaidDetails: `Requer configuração com o seu banco, mas vamos orientar você. Isso geralmente é limitado a empresas maiores.`, directFeedDetails: 'A abordagem mais simples. Conecte-se imediatamente usando suas credenciais principais. Esse método é o mais comum.', enableFeed: { - title: (provider: string) => `Ative seu feed ${provider}`, + title: (provider: string) => `Ative seu feed do ${provider}`, heading: 'Temos uma integração direta com o emissor do seu cartão e podemos importar seus dados de transação para o Expensify de forma rápida e precisa.\n\nPara começar, simplesmente:', visa: 'Temos integrações globais com a Visa, embora a elegibilidade varie de acordo com o banco e o programa do cartão.\n\nPara começar, basta:', @@ -7240,6 +7239,7 @@ Exija detalhes de despesas como recibos e descrições, defina limites e padrõe hold: 'Esta despesa foi colocada em espera', resolvedDuplicates: 'duplicata resolvida', companyCardRequired: 'Compras com cartão da empresa obrigatórias', + noRoute: 'Selecione um endereço válido', }, reportViolations: { [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: ({fieldName}: RequiredFieldParams) => `${fieldName} é obrigatório`, diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 295670c47ecc8..49a3e5ea1998a 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -255,7 +255,6 @@ const translations: TranslationDeepObject = { searchWithThreeDots: '搜索…', next: '下一步', previous: '上一步', - // @context Navigation button that returns the user to the previous screen. Should be interpreted as a UI action label. goBack: '返回', create: '创建', add: '添加', @@ -680,12 +679,12 @@ const translations: TranslationDeepObject = { protectedPDFNotSupported: '不支持受密码保护的 PDF', attachmentImageResized: '此图片已调整大小以供预览。下载以查看完整分辨率。', attachmentImageTooLarge: '此图片过大,无法在上传前预览。', - tooManyFiles: (fileLimit: number) => `一次最多只能上传 ${fileLimit} 个文件。`, + tooManyFiles: (fileLimit: number) => `您一次最多只能上传 ${fileLimit} 个文件。`, sizeExceededWithValue: ({maxUploadSizeInMB}: SizeExceededParams) => `文件超过 ${maxUploadSizeInMB} MB。请重试。`, someFilesCantBeUploaded: '某些文件无法上传', sizeLimitExceeded: ({maxUploadSizeInMB}: SizeExceededParams) => `文件大小必须小于 ${maxUploadSizeInMB} MB。更大的文件将不会被上传。`, maxFileLimitExceeded: '您一次最多可以上传 30 张收据。超出部分将不会被上传。', - unsupportedFileType: (fileType: string) => `不支持 ${fileType} 文件。只有支持的文件类型会被上传。`, + unsupportedFileType: (fileType: string) => `不支持 ${fileType} 文件。只有受支持的文件类型会被上传。`, learnMoreAboutSupportedFiles: '了解支持的格式详情。', passwordProtected: '不支持受密码保护的 PDF 文件。只有受支持的文件会被上传。', }, @@ -958,8 +957,8 @@ const translations: TranslationDeepObject = { chooseSpreadsheetMultiLevelTag: `选择要导入的电子表格文件。了解更多有关支持的文件格式的信息。`, fileContainsHeader: '文件包含列标题', column: (name: string) => `列 ${name}`, - fieldNotMapped: (fieldName: string) => `哎呀!必填字段(“${fieldName}”)尚未映射。请检查后重试。`, - singleFieldMultipleColumns: (fieldName: string) => `哎呀!您已将单个字段(“${fieldName}”)映射到了多个列。请检查后重试。`, + fieldNotMapped: (fieldName: string) => `哎呀!一个必填字段(“${fieldName}”)尚未映射。请检查后重试。`, + singleFieldMultipleColumns: (fieldName: string) => `糟糕!你已将单个字段(“${fieldName}”)映射到多个列。请检查后重试。`, emptyMappedField: (fieldName: string) => `哎呀!字段(“${fieldName}”)包含一个或多个空值。请检查后重试。`, importSuccessfulTitle: '导入成功', importCategoriesSuccessfulDescription: (categories: number) => (categories > 1 ? `已添加 ${categories} 个类别。` : '已添加 1 个类别。'), @@ -977,7 +976,7 @@ const translations: TranslationDeepObject = { }, importTagsSuccessfulDescription: (tags: number) => (tags > 1 ? `已添加 ${tags} 个标签。` : '已添加 1 个标签。'), importMultiLevelTagsSuccessfulDescription: '已添加多级标签。', - importPerDiemRatesSuccessfulDescription: (rates: number) => (rates > 1 ? `已添加 ${rates} 项每日津贴标准。` : '已添加 1 个日津贴费率。'), + importPerDiemRatesSuccessfulDescription: (rates: number) => (rates > 1 ? `已添加 ${rates} 条每日补贴标准。` : '已添加 1 条每日补贴标准。'), importFailedTitle: '导入失败', importFailedDescription: '请确保所有字段均已正确填写,然后重试。如果问题仍然存在,请联系 Concierge。', importDescription: '通过单击下面每个已导入列旁边的下拉菜单,选择要从电子表格映射的字段。', @@ -1095,7 +1094,7 @@ const translations: TranslationDeepObject = { movedFromReport: ({reportName}: MovedFromReportParams) => `移动了一笔报销${reportName ? `来自 ${reportName}` : ''}`, movedTransactionTo: ({reportUrl, reportName}: MovedTransactionParams) => `已移动此报销${reportName ? `到 ${reportName}` : ''}`, movedTransactionFrom: ({reportUrl, reportName}: MovedTransactionParams) => `已移动此报销${reportName ? `来自 ${reportName}` : ''}`, - movedUnreportedTransaction: ({reportUrl}: MovedTransactionParams) => `已将此费用从您的个人空间中移出`, + movedUnreportedTransaction: ({reportUrl}: MovedTransactionParams) => `已将此费用从你的个人空间中移出`, unreportedTransaction: ({reportUrl}: MovedTransactionParams) => `已将此报销移动到你的个人空间`, movedAction: ({shouldHideMovedReportUrl, movedReportUrl, newParentReportUrl, toPolicyName}: MovedActionParams) => { if (shouldHideMovedReportUrl) { @@ -3541,7 +3540,7 @@ ${ flight: '航班', flightDetails: { passenger: '乘客', - layover: (layover: string) => `在此航班前,你有一段${layover}的中转停留时间`, + layover: (layover: string) => `在此航班前,您有${layover}的中转停留`, takeOff: '起飞', landing: '登录页', seat: '座位', @@ -4537,18 +4536,19 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM [CONST.INTEGRATION_ENTITY_MAP_TYPES.NETSUITE_DEFAULT]: { label: 'NetSuite 员工默认值', description: '未导入到 Expensify,在导出时应用', - footerContent: (importField: string) => `如果您在 NetSuite 中使用 ${importField},我们将在导出至 Expense Report 或 Journal Entry 时应用员工记录中设置的默认值。`, + footerContent: (importField: string) => `${startCase(importField)} 选择将应用于员工报表中的所有报销。`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.TAG]: { label: '标签', description: '逐项级别', - footerContent: (importField: string) => `${startCase(importField)} 将可在员工报表中的每一笔单独费用上进行选择。`, + footerContent: (importField: string) => `${startCase(importField)} 选择将应用于员工报表中的所有报销。`, }, [CONST.INTEGRATION_ENTITY_MAP_TYPES.REPORT_FIELD]: { label: '报表字段', description: '报表级别', - footerContent: (importField: string) => `${startCase(importField)} 选择将应用于员工报表上的所有报销费用。`, + footerContent: (importField: string) => `${startCase(importField)} 选择将应用于员工报表中的所有报销。`, }, + footerContent: (importField: string) => `${startCase(importField)} 选择将应用于员工报表中的所有报销。`, }, }, }, @@ -4624,7 +4624,7 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM commercialFeedPlaidDetails: `需要与你的银行进行设置,但我们会指导你完成。此功能通常仅限较大规模的公司使用。`, directFeedDetails: '最简单的方法。立即使用您的主凭证进行连接。此方法最为常见。', enableFeed: { - title: (provider: string) => `启用您的 ${provider} 订阅源`, + title: (provider: string) => `启用你的 ${provider} 数据源`, heading: '我们已与您的发卡机构进行了直接集成,可以快速且准确地将您的交易数据导入 Expensify。\n\n要开始使用,只需:', visa: '我们与 Visa 有全球集成,但资格取决于发卡银行和卡片项目。\n\n要开始使用,只需:', mastercard: '我们与 Mastercard 建立了全球集成,但可用性会因银行和卡片计划而异。\n\n要开始使用,只需:', @@ -7085,6 +7085,7 @@ ${reportName} hold: '此报销已被搁置', resolvedDuplicates: '已解决重复项', companyCardRequired: '需要公司卡消费', + noRoute: '请选择一个有效的地址', }, reportViolations: { [CONST.REPORT_VIOLATIONS.FIELD_REQUIRED]: ({fieldName}: RequiredFieldParams) => `${fieldName}为必填项`, From faf9ab8d1b4062cc7750b0895f91c176f4f1126c Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Thu, 15 Jan 2026 21:30:38 +0100 Subject: [PATCH 24/25] ts --- src/languages/de.ts | 1 - src/languages/fr.ts | 1 - src/languages/it.ts | 1 - src/languages/nl.ts | 1 - src/languages/pl.ts | 1 - src/languages/pt-BR.ts | 1 - src/languages/zh-hans.ts | 1 - 7 files changed, 7 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index f6ae647da174d..0ec4166f0989b 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -4719,7 +4719,6 @@ _Für ausführlichere Anweisungen [besuchen Sie unsere Hilfeseite](${CONST.NETSU description: 'Berichtsebene', footerContent: ({importField}: ImportFieldParams) => `${startCase(importField)}-Auswahl gilt für alle Ausgaben auf dem Bericht eines Mitarbeiters.`, }, - footerContent: (importField: string) => `${startCase(importField)}-Auswahl wird auf alle Ausgaben in dem Bericht eines Mitarbeiters angewendet.`, }, }, }, diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 389fc0711f7d3..307c341f65bd6 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -4724,7 +4724,6 @@ _Pour des instructions plus détaillées, [visitez notre site d’aide](${CONST. description: 'Niveau de rapport', footerContent: ({importField}: ImportFieldParams) => `La sélection ${startCase(importField)} s’appliquera à toutes les dépenses sur le rapport d’un employé.`, }, - footerContent: (importField: string) => `La sélection ${startCase(importField)} s’appliquera à toutes les dépenses du rapport d’un employé.`, }, }, }, diff --git a/src/languages/it.ts b/src/languages/it.ts index fc85c30cd3dce..042ecad874e42 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -4706,7 +4706,6 @@ _Per istruzioni più dettagliate, [visita il nostro sito di assistenza](${CONST. description: 'Livello del report', footerContent: ({importField}: ImportFieldParams) => `La selezione ${startCase(importField)} verrà applicata a tutte le spese nel rendiconto di un dipendente.`, }, - footerContent: (importField: string) => `${startCase(importField)} la selezione verrà applicata a tutte le spese nel report di un dipendente.`, }, }, }, diff --git a/src/languages/nl.ts b/src/languages/nl.ts index f8004f7246848..0701af7186f26 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -4703,7 +4703,6 @@ _Voor gedetailleerdere instructies, [bezoek onze helpsite](${CONST.NETSUITE_IMPO description: 'Rapportniveau', footerContent: ({importField}: ImportFieldParams) => `${startCase(importField)}-selectie is van toepassing op alle onkosten op het rapport van een werknemer.`, }, - footerContent: (importField: string) => `${startCase(importField)}-selectie wordt toegepast op alle uitgaven op het rapport van een werknemer.`, }, }, }, diff --git a/src/languages/pl.ts b/src/languages/pl.ts index 78639d885d0f5..150f9e7a33837 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -4691,7 +4691,6 @@ _Aby uzyskać bardziej szczegółowe instrukcje, [odwiedź naszą stronę pomocy description: 'Poziom raportu', footerContent: ({importField}: ImportFieldParams) => `Wybór ${startCase(importField)} zostanie zastosowany do wszystkich wydatków w raporcie pracownika.`, }, - footerContent: (importField: string) => `Wybrana opcja ${startCase(importField)} zostanie zastosowana do wszystkich wydatków w raporcie pracownika.`, }, }, }, diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index 1b8bb980b8e33..d7fc8da1fbfb6 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -4691,7 +4691,6 @@ _Para instruções mais detalhadas, [visite nosso site de ajuda](${CONST.NETSUIT description: 'Nível de relatório', footerContent: ({importField}: ImportFieldParams) => `A seleção de ${startCase(importField)} será aplicada a todas as despesas no relatório de um funcionário.`, }, - footerContent: (importField: string) => `A seleção ${startCase(importField)} será aplicada a todas as despesas no relatório de um funcionário.`, }, }, }, diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 797bc2104b6df..8558c8c3fec59 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -4607,7 +4607,6 @@ _如需更详细的说明,请[访问我们的帮助网站](${CONST.NETSUITE_IM description: '报表级别', footerContent: ({importField}: ImportFieldParams) => `${startCase(importField)} 选择将应用于员工报表上的所有报销费用。`, }, - footerContent: (importField: string) => `${startCase(importField)} 选择将应用于员工报表中的所有报销。`, }, }, }, From e37688e18705a6d9be78ebe40ea7821f6900ea4f Mon Sep 17 00:00:00 2001 From: Abdelhafidh Belalia <16493223+s77rt@users.noreply.github.com> Date: Thu, 15 Jan 2026 21:50:45 +0100 Subject: [PATCH 25/25] ts --- src/components/Search/index.tsx | 3 ++- src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/Search/index.tsx b/src/components/Search/index.tsx index 882d151573f16..f8b9a9beaed1c 100644 --- a/src/components/Search/index.tsx +++ b/src/components/Search/index.tsx @@ -451,13 +451,14 @@ function Search({ currentUserEmail: email ?? '', translate, formatPhoneNumber, + bankAccountList, isActionLoadingSet, }); return {...item, transactions: transactions1 as TransactionListItemType[]}; }); return enriched; - }, [validGroupBy, isExpenseReportType, baseFilteredData, groupByTransactionSnapshots, accountID, email, translate, formatPhoneNumber, isActionLoadingSet]); + }, [validGroupBy, isExpenseReportType, baseFilteredData, groupByTransactionSnapshots, accountID, email, translate, formatPhoneNumber, bankAccountList, isActionLoadingSet]); const hasLoadedAllTransactions = useMemo(() => { if (!validGroupBy) { diff --git a/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx b/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx index a40afc8e479d1..d343130b842aa 100644 --- a/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx +++ b/src/pages/iou/request/step/IOURequestStepDistanceOdometer.tsx @@ -105,6 +105,8 @@ function IOURequestStepDistanceOdometer({ const [skipConfirmation] = useOnyx(`${ONYXKEYS.COLLECTION.SKIP_CONFIRMATION}${transactionID}`, {canBeMissing: true}); const [parentReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${getNonEmptyStringOnyxID(report?.parentReportID)}`, {canBeMissing: true}); const policy = usePolicy(report?.policyID); + const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policy?.id}`, {canBeMissing: true}); + const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policy?.id}`, {canBeMissing: true}); const personalPolicy = usePersonalPolicy(); const defaultExpensePolicy = useDefaultExpensePolicy(); @@ -339,6 +341,8 @@ function IOURequestStepDistanceOdometer({ // Not required for odometer distance request transactionBackup: undefined, policy, + policyTagList: policyTags, + policyCategories, currentUserAccountIDParam, currentUserEmailParam, isASAPSubmitBetaEnabled: false,