From 9765cf3feee4055128cc92ed6817a725bbefbcdb Mon Sep 17 00:00:00 2001 From: Maurice Date: Tue, 20 Oct 2026 20:44:25 +0200 Subject: [PATCH] feat(tours): give a tour its start and end in the day plan A tour on a day had no place in the day's timeline: its time was not tied to the slot it was planned in, so several hikes on one day could not be lined up. A tour now sits there the way a place does, on the times of its day entry, one model for every tour type. With a start and a known time (the planned total, else the estimate of an import) its end follows as the start plus that time; it is shown fainter, marked with an approximate sign where it comes from an estimate, and says in its tooltip how it was worked out. An end typed by hand wins. A day that holds one stage of a multi-day tour gets no end from the whole route. When the end runs past the start of the day's next timed item, the row says so in one quiet line, as a late connection between two city walks does; nothing is moved. The existing time fields of the day entry, on the desktop form and the phone sheet, say what the end works out to while none is typed. City walks keep their arrivals per station. The logic lives in one module both day plans run on. --- client/scripts/feature-pairs.json | 6 + .../Planner/DayPlanPlaceSubline.tsx | 12 +- .../Planner/DayPlanSidebar.tours.test.tsx | 25 ++- .../src/components/Planner/DayPlanSidebar.tsx | 7 +- .../src/components/Planner/PlaceFormModal.tsx | 10 +- .../Tours/TourDayTimeParts.test.tsx | 97 +++++++++++ .../src/components/Tours/TourDayTimeParts.tsx | 153 ++++++++++++++++++ .../src/components/Tours/tourDayTime.test.ts | 152 +++++++++++++++++ client/src/components/Tours/tourDayTime.ts | 141 ++++++++++++++++ .../screens/trip/plan/MPlanTimeline.tsx | 1 + .../plan/MPlanTimelineRows.tours.test.tsx | 14 ++ .../screens/trip/plan/MPlanTimelineRows.tsx | 9 +- .../screens/trip/plan/useMPlanTimeline.ts | 6 +- .../screens/trip/sheets/PlTimeFields.tsx | 5 + client/src/pages/TripPlannerPage.tsx | 2 +- .../unit/mobile/trip/MPlanTimeline.test.tsx | 9 ++ .../unit/mobile/trip/PlTimeFields.test.tsx | 38 ++++- .../trip/useMPlanTimeline.transit.test.tsx | 15 ++ shared/src/i18n/ar/tours.ts | 6 + shared/src/i18n/az/tours.ts | 7 + shared/src/i18n/br/tours.ts | 7 + shared/src/i18n/ca/tours.ts | 6 + shared/src/i18n/cs/tours.ts | 6 + shared/src/i18n/de/tours.ts | 7 + shared/src/i18n/en/tours.ts | 7 + shared/src/i18n/es/tours.ts | 7 + shared/src/i18n/et/tours.ts | 6 + shared/src/i18n/fr/tours.ts | 7 + shared/src/i18n/gr/tours.ts | 7 + shared/src/i18n/hu/tours.ts | 6 + shared/src/i18n/id/tours.ts | 6 + shared/src/i18n/it/tours.ts | 7 + shared/src/i18n/ja/tours.ts | 6 + shared/src/i18n/ko/tours.ts | 6 + shared/src/i18n/nl/tours.ts | 7 + shared/src/i18n/pl/tours.ts | 6 + shared/src/i18n/ru/tours.ts | 7 + shared/src/i18n/sk/tours.ts | 6 + shared/src/i18n/sv/tours.ts | 6 + shared/src/i18n/th/tours.ts | 6 + shared/src/i18n/tours-locales.spec.ts | 2 +- shared/src/i18n/tr/tours.ts | 6 + shared/src/i18n/uk/tours.ts | 7 + shared/src/i18n/vi/tours.ts | 6 + shared/src/i18n/zh-TW/tours.ts | 6 + shared/src/i18n/zh/tours.ts | 6 + 46 files changed, 865 insertions(+), 12 deletions(-) create mode 100644 client/src/components/Tours/TourDayTimeParts.test.tsx create mode 100644 client/src/components/Tours/TourDayTimeParts.tsx create mode 100644 client/src/components/Tours/tourDayTime.test.ts create mode 100644 client/src/components/Tours/tourDayTime.ts diff --git a/client/scripts/feature-pairs.json b/client/scripts/feature-pairs.json index 74d26cc5f..3d3ec7809 100644 --- a/client/scripts/feature-pairs.json +++ b/client/scripts/feature-pairs.json @@ -586,6 +586,12 @@ "desktop": ["src/components/Planner/DayPlanSidebar.tsx"], "mobile": ["src/mobile/screens/trip/plan/useMPlanTimeline.ts"] }, + { + "feature": "A tour's start and end in the day plan, its end worked out from its time, and an end that runs into the next timed item", + "hooks": ["src/components/Tours/tourDayTime.ts"], + "desktop": ["src/components/Planner/DayPlanSidebar.tsx"], + "mobile": ["src/mobile/screens/trip/plan/useMPlanTimeline.ts"] + }, { "feature": "Reservation form incl. hotel accommodation creation", "hooks": ["src/components/Planner/reservationFormModel.ts", "src/components/Planner/useReservationForm.ts"], diff --git a/client/src/components/Planner/DayPlanPlaceSubline.tsx b/client/src/components/Planner/DayPlanPlaceSubline.tsx index 2a63720aa..7fe30f007 100644 --- a/client/src/components/Planner/DayPlanPlaceSubline.tsx +++ b/client/src/components/Planner/DayPlanPlaceSubline.tsx @@ -9,6 +9,7 @@ import { fs } from '../shared/DialogShell'; import { stageNumberOf } from '../Tours/stages/stageNames'; import { TourStageDayPills } from '../Tours/stages/TourStageDayPills'; import { TourDayRowPills } from '../Tours/TourDayRowFacts'; +import { TourDayTimeText } from '../Tours/TourDayTimeParts'; import { movingTimeLabel } from '../Tours/tourFacts'; import { formatPlannedTourDuration, plannedTotalText, type TourPlannedTimes } from '../Tours/tourPresentation'; import { WalkProgressPill } from '../Tours/walk/WalkStations'; @@ -120,7 +121,16 @@ export function DayPlanPlaceSubline({ // One line like the row's name: when the hover controls come out, the description // gives way and the pills keep their place instead of wrapping onto a second line.
- {place.place_time && ( + {place.place_time && dayRowTour && ( + // TIME-02: a tour's end, typed or worked out from its start and its time. + + + + )} + {place.place_time && !dayRowTour && ( {formatTime(place.place_time, locale, timeFormat)} {place.end_time ? ` – ${formatTime(place.end_time, locale, timeFormat)}` : ''} diff --git a/client/src/components/Planner/DayPlanSidebar.tours.test.tsx b/client/src/components/Planner/DayPlanSidebar.tours.test.tsx index 6f327caef..b50a2e2b5 100644 --- a/client/src/components/Planner/DayPlanSidebar.tours.test.tsx +++ b/client/src/components/Planner/DayPlanSidebar.tours.test.tsx @@ -394,7 +394,12 @@ function walkTour(place: { id: number; name: string }, overrides: Partial void }) { +function walkDayProps(extra: { + reservations?: Reservation[]; + withCafe?: boolean; + onPlanTransitLeg?: () => void; + firstWalk?: Partial; +}) { const day = buildDay({ id: 10, date: '2025-06-01', title: 'Day 1' }); const assignments = { '10': [ @@ -415,7 +420,7 @@ function walkDayProps(extra: { reservations?: Reservation[]; withCafe?: boolean; ...(extra.withCafe ? [buildAssignment({ id: 203, day_id: 10, order_index: 2, place: cafe })] : []), ], }; - const tours = [walkTour(oldTown), walkTour(harbour)]; + const tours = [walkTour(oldTown, extra.firstWalk), walkTour(harbour)]; return makeDefaultProps({ days: [day], places: [oldTown, harbour, cafe], @@ -559,4 +564,20 @@ describe('DayPlanSidebar, public transit between two city walks (CW-08)', () => expect(screen.getByTestId('transit-walk-late')).toHaveTextContent('Arrives after Harbour starts at 13:00'); expect(onPlanTransitLeg).not.toHaveBeenCalled(); }); + + it('TIME-02-017: a tour with a start shows its end worked out from its time, and nothing runs over', () => { + render(); + + const ends = screen.getAllByTestId('tour-day-end-worked-out'); + expect(ends[0]).toHaveTextContent('12:15'); + expect(ends[0]).toHaveAccessibleName('Ends at 12:15, worked out from the start and the tour time of 2 h 15 min'); + expect(screen.queryByTestId('tour-day-overrun')).not.toBeInTheDocument(); + }); + + it('TIME-02-018: an end that runs past the start of the next timed item is said under the tour, nothing moved', () => { + render(); + + expect(screen.getAllByTestId('tour-day-end-worked-out')[0]).toHaveTextContent('14:00'); + expect(screen.getByTestId('tour-day-overrun')).toHaveTextContent('Ends at 14:00, after Harbour starts at 13:00'); + }); }); diff --git a/client/src/components/Planner/DayPlanSidebar.tsx b/client/src/components/Planner/DayPlanSidebar.tsx index f02c09473..0c04bc74b 100644 --- a/client/src/components/Planner/DayPlanSidebar.tsx +++ b/client/src/components/Planner/DayPlanSidebar.tsx @@ -11,6 +11,8 @@ import { ChevronDown, ChevronRight, ChevronUp, Compass, RotateCcw, ExternalLink, import type { PlannedTransitLeg } from './transitLeg' import { useTransitLegPlanner } from './useTransitLegPlanner' import { WalkLateLine } from '../Tours/walk/WalkTransitNote' +import { TourOverrunLine } from '../Tours/TourDayTimeParts' +import { dayItemOverrun } from '../Tours/tourDayTime' import { optimizeRoute, generateGoogleMapsUrl, generateCoMapsUrl } from '../Map/RouteCalculator' import { dayViaLookup, type ViasByDay } from '../../utils/dayRouteVias' import { routeLeg, type LegEnd } from '../Map/dayRouteLeg' @@ -1414,8 +1416,9 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar(props: DayPlanSidebarP // The extra connector-menu entry (#1281 follow-up): public transit for this leg while a handler is wired (day has // dates); between two city walks from the end of the one it leaves, `originId`, as two can end at one spot (CW-08). + const tourOfPlace = (placeId: number) => props.plannedTourDurations?.get(placeId)?.tour const transitLegs = useTransitLegPlanner({ tripId, assignments, accommodations, reservations, places, toursEnabled, - onPlan: onPlanTransitLeg, tourOf: placeId => props.plannedTourDurations?.get(placeId)?.tour }) + onPlan: onPlanTransitLeg, tourOf: tourOfPlace }) const transitLegPlanner = (dayId: number, seg?: RouteSegment, originId?: number) => { const leg = transitLegs.legFor(dayId, seg) return leg ? () => transitLegs.plan(dayId, leg, originId) : undefined @@ -2034,6 +2037,8 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar(props: DayPlanSidebarP )}
+ {/* TIME-02: the tour's end runs past the start of the day's next timed item. */} + {dayRowTour && } {assignment.notes && ( // Day-specific note on this stop (#2163) — one muted // caption line so the timeline shows the note exists diff --git a/client/src/components/Planner/PlaceFormModal.tsx b/client/src/components/Planner/PlaceFormModal.tsx index 4fb97c966..fa33afb50 100644 --- a/client/src/components/Planner/PlaceFormModal.tsx +++ b/client/src/components/Planner/PlaceFormModal.tsx @@ -1,5 +1,5 @@ import { useState, useEffect, useRef, useMemo, useCallback, useId, type ReactNode } from 'react' -import type { RoadtripStopType } from '@trek/shared' +import type { RoadtripStopType, TourListItem } from '@trek/shared' import NoteFormatToolbar from '../shared/NoteFormatToolbar' import { useAuthStore } from '../../store/authStore' import { useAddonStore } from '../../store/addonStore' @@ -43,6 +43,7 @@ import { import { EditorField, GRID_2, INPUT, LABEL, PANEL, PillSelect, TEXTAREA } from '../shared/dialogParts' import { SoftPill, tintOf } from './planParts' import { WHITE_BUTTON } from './placeDialogParts' +import { TourEndHint } from '../Tours/TourDayTimeParts' // The submit payload mirrors the form, but lat/lng are parsed to numbers and // category_id is normalised, plus any files chosen before the place existed. @@ -85,6 +86,8 @@ interface PlaceFormModalProps { serviceStop?: ServiceStopMode | null /** Road trip mode is on, where a visit's End is when the drive leaves it. */ roadtripActive?: boolean + /** TIME-02: the tour this place is, whose end the time fields work out from its start; absent for a plain place. */ + tour?: TourListItem | null } @@ -843,6 +846,7 @@ export default function PlaceFormModal(props: PlaceFormModalProps) { dayAssignments={S.dayAssignments} hasTimeError={hasTimeError} endIsLeave={!!props.roadtripActive} + tour={props.tour} t={t} /> @@ -1166,10 +1170,11 @@ interface TimeSectionProps { /** On a road trip the End is when the drive leaves, which the field says. In Days it * stays the plain label it has always been: nothing is scheduled off it there. */ endIsLeave: boolean + tour?: TourListItem | null t: Translate } -function TimeSection({ form, handleChange, assignmentId, dayAssignments, hasTimeError, endIsLeave, t }: TimeSectionProps) { +function TimeSection({ form, handleChange, assignmentId, dayAssignments, hasTimeError, endIsLeave, tour, t }: TimeSectionProps) { const collisions = useMemo( () => timeCollisions(assignmentId, dayAssignments, form.place_time, form.end_time), [assignmentId, dayAssignments, form.place_time, form.end_time], @@ -1196,6 +1201,7 @@ function TimeSection({ form, handleChange, assignmentId, dayAssignments, hasTime /> + a.id === assignmentId)} tour={tour} /> {hasTimeError && (
)} + {chrome.editing && ( diff --git a/client/src/mobile/screens/trip/plan/useMPlanTimeline.ts b/client/src/mobile/screens/trip/plan/useMPlanTimeline.ts index d8fa847a5..1c88767e3 100644 --- a/client/src/mobile/screens/trip/plan/useMPlanTimeline.ts +++ b/client/src/mobile/screens/trip/plan/useMPlanTimeline.ts @@ -11,6 +11,7 @@ import { getDisplayTimeForDay, getMergedItems, getTransportForDay, hasCarrierEnd import { dayCoMapsUrl, dayExportStops, dayGoogleMapsUrl, optimizeDayOrder, type DayCarrier } from '../../../../components/Planner/dayRoute' import type { PlannedTransitLeg, TransitLeg } from '../../../../components/Planner/transitLeg' import { useTransitLegPlanner } from '../../../../components/Planner/useTransitLegPlanner' +import { dayItemOverrun } from '../../../../components/Tours/tourDayTime' import { buildPlanRows, breaksChronology, findUpNext, hotelChipsForDay, hotelLegsForDay, itemHasTime, type HotelLegs, type PlanRow, type TransportEntry, @@ -377,6 +378,9 @@ export function useMPlanTimeline(planner: TripPlanner) { // A saved journey that now arrives after the walk it leads to starts (CW-08). const journeyLate = useCallback((item: MergedItem) => (day ? lateBefore(day, item.data, merged.slice(merged.indexOf(item) + 1)) : null), [day, lateBefore, merged]) + // A tour whose end runs past the start of the day's next timed item (TIME-02). + const tourOverrun = useCallback((item: MergedItem) => + (day ? dayItemOverrun(merged, merged.indexOf(item), day.id, tourOf) : null), [day, merged, tourOf]) return { day, rows, hotelLegs, merged, hotelChips, weather, weatherTemp, upNext, @@ -387,7 +391,7 @@ export function useMPlanTimeline(planner: TripPlanner) { moveRowTo, addPlace, addBooking, addTransport, optimize, canExportRoute, exportGoogleMaps, exportCoMaps, renameDay, fullPlaceOf, - routeModeOptions, setLegMode, transitLegFor, planTransitLeg, journeyLate, + routeModeOptions, setLegMode, transitLegFor, planTransitLeg, journeyLate, tourOverrun, } } diff --git a/client/src/mobile/screens/trip/sheets/PlTimeFields.tsx b/client/src/mobile/screens/trip/sheets/PlTimeFields.tsx index 6e63f8b26..cb5e8c180 100644 --- a/client/src/mobile/screens/trip/sheets/PlTimeFields.tsx +++ b/client/src/mobile/screens/trip/sheets/PlTimeFields.tsx @@ -4,6 +4,7 @@ import { Eyebrow } from './PlSheetChrome' import CustomTimePicker from '../../../../components/shared/CustomTimePicker' import { timeCollisions } from '../../../../components/Planner/PlaceFormModal.helpers' import type { Assignment } from '../../../../types' +import { TourEndHint } from '../../../../components/Tours/TourDayTimeParts' import type { TripPlanner } from '../MTripShell' interface PlTimeFieldsProps { @@ -39,6 +40,9 @@ export default function PlTimeFields({ () => timeCollisions(assignmentId, dayAssignments, startTime, endTime), [assignmentId, dayAssignments, startTime, endTime], ) + // TIME-02: a tour's end follows from its start and its time while none is typed. + const assignment = dayAssignments.find(a => a.id === assignmentId) + const tour = assignment ? planner.tours.find(item => item.place_id === assignment.place_id) : undefined return (
@@ -55,6 +59,7 @@ export default function PlTimeFields({ )}
+ {hasTimeError && (
diff --git a/client/src/pages/TripPlannerPage.tsx b/client/src/pages/TripPlannerPage.tsx index fd28cb067..87c791f83 100644 --- a/client/src/pages/TripPlannerPage.tsx +++ b/client/src/pages/TripPlannerPage.tsx @@ -831,7 +831,7 @@ function TripPlannerPageDesktop(): React.ReactElement | null { /> )} - { setShowPlaceForm(false); setEditingPlace(null); setEditingAssignmentId(null); setPrefillCoords(null); setServiceStopForm(false) }} onSave={handleSavePlace} place={editingPlace} prefillCoords={prefillCoords} assignmentId={editingAssignmentId} dayAssignments={editingPlace ? Object.values(assignments).flat() : []} tripId={tripId} categories={categories} onCategoryCreated={cat => tripActions.addCategory?.(cat)} isMobile={isMobile} onOpenExpense={openBookingExpense} serviceStop={serviceStopMode} roadtripActive={roadtripActive} /> + { setShowPlaceForm(false); setEditingPlace(null); setEditingAssignmentId(null); setPrefillCoords(null); setServiceStopForm(false) }} onSave={handleSavePlace} place={editingPlace} prefillCoords={prefillCoords} assignmentId={editingAssignmentId} dayAssignments={editingPlace ? Object.values(assignments).flat() : []} tripId={tripId} categories={categories} onCategoryCreated={cat => tripActions.addCategory?.(cat)} isMobile={isMobile} onOpenExpense={openBookingExpense} serviceStop={serviceStopMode} roadtripActive={roadtripActive} tour={editingPlace ? tours.find(item => item.place_id === editingPlace.id) ?? null : null} /> setShowTripForm(false)} diff --git a/client/tests/unit/mobile/trip/MPlanTimeline.test.tsx b/client/tests/unit/mobile/trip/MPlanTimeline.test.tsx index fe1599d42..e49008147 100644 --- a/client/tests/unit/mobile/trip/MPlanTimeline.test.tsx +++ b/client/tests/unit/mobile/trip/MPlanTimeline.test.tsx @@ -124,6 +124,7 @@ function buildTl(over: Record = {}): MPlanTimelineController { transitLegFor: vi.fn(() => null), planTransitLeg: vi.fn(), journeyLate: vi.fn(() => null), + tourOverrun: vi.fn(() => null), ...over, } as unknown as MPlanTimelineController } @@ -391,6 +392,14 @@ describe('MPlanTimeline', () => { renderTimeline({ rows: [row], merged: [item], upNext: null }, { toursEnabled: false }) expect(screen.queryByText('tours.dayRoute.endpointUnknown')).not.toBeInTheDocument() }) + + it('TIME-02-020: a tour row says when its end runs into the next timed item, read from the shared timeline logic', () => { + const validRow: PlanRow = { ...row, invalidTour: false } + const tourOverrun = vi.fn(() => ({ name: 'Lunch', time: '12:00', end: '13:00' })) + renderTimeline({ rows: [validRow], merged: [item], upNext: null, tourOverrun }, { toursEnabled: true, tours: [] }) + expect(tourOverrun).toHaveBeenCalledWith(item) + expect(screen.getByTestId('tour-day-overrun')).toBeInTheDocument() + }) }) describe('per-segment travel mode', () => { diff --git a/client/tests/unit/mobile/trip/PlTimeFields.test.tsx b/client/tests/unit/mobile/trip/PlTimeFields.test.tsx index 7bfa12667..815f60fb5 100644 --- a/client/tests/unit/mobile/trip/PlTimeFields.test.tsx +++ b/client/tests/unit/mobile/trip/PlTimeFields.test.tsx @@ -7,7 +7,7 @@ import { seedStore } from '../../../helpers/store' import { buildSettings } from '../../../helpers/factories' import { useSettingsStore } from '../../../../src/store/settingsStore' -// FE-MOB-PLTIME-001 to FE-MOB-PLTIME-015 +// FE-MOB-PLTIME-001 to FE-MOB-PLTIME-017 const planner = buildPlanner() @@ -171,4 +171,40 @@ describe('PlTimeFields', () => { setup() expect(screen.queryByText('roadtrip.stop.endIsLeave')).not.toBeInTheDocument() }) + + // TIME-02: an imported hike of 12 km, 300 m up and 500 m down, estimated at 4 h. + const hike = { + place_id: 1, name: 'Museum ridge', tour_type: 'hike', distance: 12, elevation_gain: 300, elevation_loss: 500, + duration: null, planned_duration_minutes: null, break_additional_minutes: null, difficulty: null, wanderer_ref: null, + match_confidence: 1, max_hiking_difficulty: null, planned: true, caution: false, + } + + function renderTour(startTime: string, endTime: string) { + seedStore(useSettingsStore, { settings: buildSettings({ time_format: '24h' }) }) + return render( + , + ) + } + + it('FE-MOB-PLTIME-016: a tour without a typed end says the end its start and its time work out to', () => { + renderTour('09:00', '') + expect(screen.getByTestId('tour-day-end-hint')).toHaveTextContent('13:00') + expect(screen.getByTestId('tour-day-end-hint')).toHaveTextContent('≈ 4 h') + }) + + it('FE-MOB-PLTIME-017: before a start it says one will set the end, and a typed end needs no hint', () => { + const view = renderTour('', '') + expect(screen.getByTestId('tour-day-end-hint')).toHaveTextContent('≈ 4 h') + view.unmount() + renderTour('09:00', '15:00') + expect(screen.queryByTestId('tour-day-end-hint')).not.toBeInTheDocument() + }) }) diff --git a/client/tests/unit/mobile/trip/useMPlanTimeline.transit.test.tsx b/client/tests/unit/mobile/trip/useMPlanTimeline.transit.test.tsx index 6cfdb74cb..9e04c6eb4 100644 --- a/client/tests/unit/mobile/trip/useMPlanTimeline.transit.test.tsx +++ b/client/tests/unit/mobile/trip/useMPlanTimeline.transit.test.tsx @@ -115,4 +115,19 @@ describe('useMPlanTimeline, public transit between two city walks (CW-08)', () = if (!item) throw new Error('no journey on the day') expect(result.current.journeyLate(item)).toEqual({ walk: 'Harbour', time: '13:00' }) }) + + it('FE-MOB-PLTL-HOOK-005: says a walk whose end runs past the next walk start, the desktop rule (TIME-02)', () => { + const late = buildPlanner({ + ...walkPlanner(), + tours: [{ ...walk(oldTown), planned_duration_minutes: 240 }, walk(harbour)], + } as never) + const { result } = renderHook(() => useMPlanTimeline(late)) + + const [first, second] = result.current.merged + expect(result.current.tourOverrun(first)).toEqual({ name: 'Harbour', time: '13:00', end: '14:00' }) + expect(result.current.tourOverrun(second)).toBeNull() + // In time, nothing is said. + const { result: inTime } = renderHook(() => useMPlanTimeline(walkPlanner())) + expect(inTime.current.tourOverrun(inTime.current.merged[0])).toBeNull() + }) }) diff --git a/shared/src/i18n/ar/tours.ts b/shared/src/i18n/ar/tours.ts index 0d547eda5..e91f94ecc 100644 --- a/shared/src/i18n/ar/tours.ts +++ b/shared/src/i18n/ar/tours.ts @@ -1370,5 +1370,11 @@ const tours: TranslationStrings = { 'tours.estimate.noHeights': 'لا يحتوي المسار على ارتفاعات، لذا لا يمكن تقدير وقته.', 'tours.estimate.basisDin': 'محسوب من المسافة والصعود والنزول وفق قاعدة لافتات المشي (DIN 33466)، دون استراحات.', 'tours.estimate.basisPace': 'محسوب من المسافة بالوتيرة المُدخلة.', + 'tours.dayTime.workedOut': 'ينتهي عند {time}، محسوبًا من البداية ومدة الجولة {total}', + 'tours.dayTime.workedOutNextDay': 'ينتهي في اليوم التالي عند {time}، محسوبًا من البداية ومدة الجولة {total}', + 'tours.dayTime.overrun': 'ينتهي عند {end}، بعد بدء {name} عند {time}', + 'tours.dayTime.nextItem': 'العنصر التالي', + 'tours.dayTime.formHint': 'إذا تُرك فارغًا تُحسب النهاية: {time}، أي البداية مضافًا إليها مدة الجولة {total}.', + 'tours.dayTime.formNoStart': 'مع وقت البداية تُحسب النهاية من مدة الجولة {total}.', }; export default tours; diff --git a/shared/src/i18n/az/tours.ts b/shared/src/i18n/az/tours.ts index ed1fa43f1..85c2bf5c8 100644 --- a/shared/src/i18n/az/tours.ts +++ b/shared/src/i18n/az/tours.ts @@ -1311,5 +1311,12 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Məsafə, qalxma və enmədən piyada nişanları qaydası ilə (DIN 33466) hesablanıb, fasilələr daxil deyil.', 'tours.estimate.basisPace': 'Məsafədən daxil edilmiş templə hesablanıb.', + 'tours.dayTime.workedOut': '{time} radələrində bitir, başlanğıc və {total} tur vaxtından hesablanıb', + 'tours.dayTime.workedOutNextDay': + 'Növbəti gün {time} radələrində bitir, başlanğıc və {total} tur vaxtından hesablanıb', + 'tours.dayTime.overrun': '{end} radələrində bitir, {name} {time} radələrində başladıqdan sonra', + 'tours.dayTime.nextItem': 'növbəti element', + 'tours.dayTime.formHint': 'Boş qalsa, son hesablanır: {time}, başlanğıc üstəgəl {total} tur vaxtı.', + 'tours.dayTime.formNoStart': 'Başlanğıc vaxtı ilə son {total} tur vaxtından hesablanır.', }; export default tours; diff --git a/shared/src/i18n/br/tours.ts b/shared/src/i18n/br/tours.ts index d269719a3..d6d62307c 100644 --- a/shared/src/i18n/br/tours.ts +++ b/shared/src/i18n/br/tours.ts @@ -1324,5 +1324,12 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Calculado pela distância, subida e descida segundo a regra das placas de trilha (DIN 33466), sem pausas.', 'tours.estimate.basisPace': 'Calculado pela distância no ritmo informado.', + 'tours.dayTime.workedOut': 'Termina às {time}, calculado pelo início e pela duração de {total}', + 'tours.dayTime.workedOutNextDay': + 'Termina no dia seguinte às {time}, calculado pelo início e pela duração de {total}', + 'tours.dayTime.overrun': 'Termina às {end}, depois que {name} começa às {time}', + 'tours.dayTime.nextItem': 'o próximo item', + 'tours.dayTime.formHint': 'Se ficar vazio, o fim é calculado: {time}, o início mais a duração de {total}.', + 'tours.dayTime.formNoStart': 'Com um horário de início, o fim é calculado pela duração de {total}.', }; export default tours; diff --git a/shared/src/i18n/ca/tours.ts b/shared/src/i18n/ca/tours.ts index d2b422495..9b5f921b2 100644 --- a/shared/src/i18n/ca/tours.ts +++ b/shared/src/i18n/ca/tours.ts @@ -1330,5 +1330,11 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Calculat a partir de la distància, la pujada i la baixada segons la regla dels senyals excursionistes (DIN 33466), sense pauses.', 'tours.estimate.basisPace': 'Calculat a partir de la distància al ritme introduït.', + 'tours.dayTime.workedOut': 'Acaba a les {time}, calculat a partir de l’inici i la durada de {total}', + 'tours.dayTime.workedOutNextDay': 'Acaba l’endemà a les {time}, calculat a partir de l’inici i la durada de {total}', + 'tours.dayTime.overrun': 'Acaba a les {end}, després que {name} comenci a les {time}', + 'tours.dayTime.nextItem': 'l’element següent', + 'tours.dayTime.formHint': 'Si es deixa buit, el final es calcula: {time}, l’inici més la durada de {total}.', + 'tours.dayTime.formNoStart': 'Amb una hora d’inici, el final es calcula a partir de la durada de {total}.', }; export default tours; diff --git a/shared/src/i18n/cs/tours.ts b/shared/src/i18n/cs/tours.ts index 6ecc24212..d5823517a 100644 --- a/shared/src/i18n/cs/tours.ts +++ b/shared/src/i18n/cs/tours.ts @@ -1315,5 +1315,11 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Vypočteno ze vzdálenosti, stoupání a klesání podle pravidla turistických rozcestníků (DIN 33466), bez přestávek.', 'tours.estimate.basisPace': 'Vypočteno ze vzdálenosti při zadaném tempu.', + 'tours.dayTime.workedOut': 'Končí v {time}, vypočteno ze začátku a doby túry {total}', + 'tours.dayTime.workedOutNextDay': 'Končí další den v {time}, vypočteno ze začátku a doby túry {total}', + 'tours.dayTime.overrun': 'Končí v {end}, až po začátku {name} v {time}', + 'tours.dayTime.nextItem': 'další položka', + 'tours.dayTime.formHint': 'Když zůstane prázdný, konec se vypočte: {time}, začátek plus doba túry {total}.', + 'tours.dayTime.formNoStart': 'Se začátkem se konec vypočte z doby túry {total}.', }; export default tours; diff --git a/shared/src/i18n/de/tours.ts b/shared/src/i18n/de/tours.ts index 51b05cf58..8f275aa96 100644 --- a/shared/src/i18n/de/tours.ts +++ b/shared/src/i18n/de/tours.ts @@ -1342,6 +1342,13 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Aus Strecke, Auf- und Abstieg nach der Wegweiser-Regel (DIN 33466) berechnet, ohne Pausen.', 'tours.estimate.basisPace': 'Aus der Strecke beim eingegebenen Tempo berechnet.', + 'tours.dayTime.workedOut': 'Endet um {time}, berechnet aus dem Start und der Tourzeit von {total}', + 'tours.dayTime.workedOutNextDay': + 'Endet am nächsten Tag um {time}, berechnet aus dem Start und der Tourzeit von {total}', + 'tours.dayTime.overrun': 'Endet um {end}, nach dem Start von {name} um {time}', + 'tours.dayTime.nextItem': 'dem nächsten Eintrag', + 'tours.dayTime.formHint': 'Leer gelassen, wird das Ende berechnet: {time}, der Start plus die Tourzeit von {total}.', + 'tours.dayTime.formNoStart': 'Mit einer Startzeit wird das Ende aus der Tourzeit von {total} berechnet.', }; export default tours; diff --git a/shared/src/i18n/en/tours.ts b/shared/src/i18n/en/tours.ts index f79c43309..52c21a41d 100644 --- a/shared/src/i18n/en/tours.ts +++ b/shared/src/i18n/en/tours.ts @@ -1297,5 +1297,12 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Worked out from distance, ascent and descent by the hiking signpost rule (DIN 33466), without breaks.', 'tours.estimate.basisPace': 'Worked out from the distance at the pace entered.', + 'tours.dayTime.workedOut': 'Ends at {time}, worked out from the start and the tour time of {total}', + 'tours.dayTime.workedOutNextDay': + 'Ends at {time} on the next day, worked out from the start and the tour time of {total}', + 'tours.dayTime.overrun': 'Ends at {end}, after {name} starts at {time}', + 'tours.dayTime.nextItem': 'the next item', + 'tours.dayTime.formHint': 'Left empty, the end is worked out: {time}, the start plus the tour time of {total}.', + 'tours.dayTime.formNoStart': 'With a start time, the end is worked out from the tour time of {total}.', }; export default tours; diff --git a/shared/src/i18n/es/tours.ts b/shared/src/i18n/es/tours.ts index 81c6909a6..48fdc1d24 100644 --- a/shared/src/i18n/es/tours.ts +++ b/shared/src/i18n/es/tours.ts @@ -1323,5 +1323,12 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Calculado a partir de la distancia, el ascenso y el descenso según la regla de las señales de senderismo (DIN 33466), sin pausas.', 'tours.estimate.basisPace': 'Calculado a partir de la distancia al ritmo introducido.', + 'tours.dayTime.workedOut': 'Termina a las {time}, calculado a partir del inicio y la duración de {total}', + 'tours.dayTime.workedOutNextDay': + 'Termina al día siguiente a las {time}, calculado a partir del inicio y la duración de {total}', + 'tours.dayTime.overrun': 'Termina a las {end}, después de que {name} empiece a las {time}', + 'tours.dayTime.nextItem': 'el siguiente elemento', + 'tours.dayTime.formHint': 'Si se deja vacío, el final se calcula: {time}, el inicio más la duración de {total}.', + 'tours.dayTime.formNoStart': 'Con una hora de inicio, el final se calcula a partir de la duración de {total}.', }; export default tours; diff --git a/shared/src/i18n/et/tours.ts b/shared/src/i18n/et/tours.ts index daaef2522..341f7403a 100644 --- a/shared/src/i18n/et/tours.ts +++ b/shared/src/i18n/et/tours.ts @@ -1306,5 +1306,11 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Arvutatud vahemaa, tõusu ja laskumise järgi matkaviitade reegli (DIN 33466) alusel, pausideta.', 'tours.estimate.basisPace': 'Arvutatud vahemaast sisestatud tempoga.', + 'tours.dayTime.workedOut': 'Lõpeb kell {time}, arvutatud algusest ja matka ajast {total}', + 'tours.dayTime.workedOutNextDay': 'Lõpeb järgmisel päeval kell {time}, arvutatud algusest ja matka ajast {total}', + 'tours.dayTime.overrun': 'Lõpeb kell {end}, pärast seda kui {name} algab kell {time}', + 'tours.dayTime.nextItem': 'järgmine punkt', + 'tours.dayTime.formHint': 'Tühjaks jättes arvutatakse lõpp: {time}, algus pluss matka aeg {total}.', + 'tours.dayTime.formNoStart': 'Algusajaga arvutatakse lõpp matka ajast {total}.', }; export default tours; diff --git a/shared/src/i18n/fr/tours.ts b/shared/src/i18n/fr/tours.ts index 416c81179..f2f46a173 100644 --- a/shared/src/i18n/fr/tours.ts +++ b/shared/src/i18n/fr/tours.ts @@ -1350,5 +1350,12 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Calculé à partir de la distance, du dénivelé positif et négatif selon la règle des panneaux de randonnée (DIN 33466), sans pauses.', 'tours.estimate.basisPace': 'Calculé à partir de la distance à l’allure saisie.', + 'tours.dayTime.workedOut': 'Se termine à {time}, calculé à partir du départ et de la durée de {total}', + 'tours.dayTime.workedOutNextDay': + 'Se termine le lendemain à {time}, calculé à partir du départ et de la durée de {total}', + 'tours.dayTime.overrun': 'Se termine à {end}, après le début de {name} à {time}', + 'tours.dayTime.nextItem': 'l’élément suivant', + 'tours.dayTime.formHint': 'Laissée vide, la fin est calculée : {time}, le départ plus la durée de {total}.', + 'tours.dayTime.formNoStart': 'Avec une heure de départ, la fin est calculée à partir de la durée de {total}.', }; export default tours; diff --git a/shared/src/i18n/gr/tours.ts b/shared/src/i18n/gr/tours.ts index bbe937af5..60cb9e5e5 100644 --- a/shared/src/i18n/gr/tours.ts +++ b/shared/src/i18n/gr/tours.ts @@ -1352,5 +1352,12 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Υπολογισμένος από απόσταση, ανάβαση και κατάβαση με τον κανόνα των πινακίδων πεζοπορίας (DIN 33466), χωρίς διαλείμματα.', 'tours.estimate.basisPace': 'Υπολογισμένος από την απόσταση με τον ρυθμό που δόθηκε.', + 'tours.dayTime.workedOut': 'Τελειώνει στις {time}, υπολογισμένο από την έναρξη και τη διάρκεια {total}', + 'tours.dayTime.workedOutNextDay': + 'Τελειώνει την επόμενη μέρα στις {time}, υπολογισμένο από την έναρξη και τη διάρκεια {total}', + 'tours.dayTime.overrun': 'Τελειώνει στις {end}, μετά την έναρξη του {name} στις {time}', + 'tours.dayTime.nextItem': 'το επόμενο στοιχείο', + 'tours.dayTime.formHint': 'Αν μείνει κενό, το τέλος υπολογίζεται: {time}, η έναρξη συν τη διάρκεια {total}.', + 'tours.dayTime.formNoStart': 'Με ώρα έναρξης, το τέλος υπολογίζεται από τη διάρκεια {total}.', }; export default tours; diff --git a/shared/src/i18n/hu/tours.ts b/shared/src/i18n/hu/tours.ts index 7bc13fae7..4cc9f2f63 100644 --- a/shared/src/i18n/hu/tours.ts +++ b/shared/src/i18n/hu/tours.ts @@ -1329,5 +1329,11 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'A távból, emelkedésből és lejtésből számolva a turistaútjelzők szabálya szerint (DIN 33466), szünetek nélkül.', 'tours.estimate.basisPace': 'A távból számolva a megadott tempóval.', + 'tours.dayTime.workedOut': 'Vége: {time}, a kezdésből és a túra {total} idejéből számolva', + 'tours.dayTime.workedOutNextDay': 'Vége másnap {time}-kor, a kezdésből és a túra {total} idejéből számolva', + 'tours.dayTime.overrun': 'Vége: {end}, miután {name} {time}-kor kezdődik', + 'tours.dayTime.nextItem': 'a következő tétel', + 'tours.dayTime.formHint': 'Üresen hagyva a vég kiszámolódik: {time}, a kezdés plusz a túra {total} ideje.', + 'tours.dayTime.formNoStart': 'Kezdési időponttal a vég a túra {total} idejéből számolódik.', }; export default tours; diff --git a/shared/src/i18n/id/tours.ts b/shared/src/i18n/id/tours.ts index a73ad0d4f..eb3107934 100644 --- a/shared/src/i18n/id/tours.ts +++ b/shared/src/i18n/id/tours.ts @@ -1303,5 +1303,11 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Dihitung dari jarak, tanjakan, dan turunan menurut aturan papan penunjuk pendakian (DIN 33466), tanpa istirahat.', 'tours.estimate.basisPace': 'Dihitung dari jarak dengan kecepatan yang dimasukkan.', + 'tours.dayTime.workedOut': 'Selesai pukul {time}, dihitung dari mulai dan waktu tur {total}', + 'tours.dayTime.workedOutNextDay': 'Selesai keesokan harinya pukul {time}, dihitung dari mulai dan waktu tur {total}', + 'tours.dayTime.overrun': 'Selesai pukul {end}, setelah {name} dimulai pukul {time}', + 'tours.dayTime.nextItem': 'item berikutnya', + 'tours.dayTime.formHint': 'Jika dibiarkan kosong, akhir dihitung: {time}, waktu mulai ditambah waktu tur {total}.', + 'tours.dayTime.formNoStart': 'Dengan waktu mulai, akhir dihitung dari waktu tur {total}.', }; export default tours; diff --git a/shared/src/i18n/it/tours.ts b/shared/src/i18n/it/tours.ts index 54f6d02da..6cb24e79d 100644 --- a/shared/src/i18n/it/tours.ts +++ b/shared/src/i18n/it/tours.ts @@ -1337,5 +1337,12 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Calcolato da distanza, salita e discesa secondo la regola dei cartelli escursionistici (DIN 33466), senza pause.', 'tours.estimate.basisPace': 'Calcolato dalla distanza al ritmo inserito.', + 'tours.dayTime.workedOut': 'Finisce alle {time}, calcolato dalla partenza e dalla durata di {total}', + 'tours.dayTime.workedOutNextDay': + 'Finisce il giorno dopo alle {time}, calcolato dalla partenza e dalla durata di {total}', + 'tours.dayTime.overrun': 'Finisce alle {end}, dopo l’inizio di {name} alle {time}', + 'tours.dayTime.nextItem': 'la voce successiva', + 'tours.dayTime.formHint': 'Se lasciata vuota, la fine viene calcolata: {time}, la partenza più la durata di {total}.', + 'tours.dayTime.formNoStart': 'Con un orario di partenza, la fine viene calcolata dalla durata di {total}.', }; export default tours; diff --git a/shared/src/i18n/ja/tours.ts b/shared/src/i18n/ja/tours.ts index 6d48601ff..6ffbb758e 100644 --- a/shared/src/i18n/ja/tours.ts +++ b/shared/src/i18n/ja/tours.ts @@ -1271,5 +1271,11 @@ const tours: TranslationStrings = { 'tours.estimate.noHeights': 'トラックに標高データがないため、時間を推定できません。', 'tours.estimate.basisDin': '距離・登り・下りから登山道標識の基準(DIN 33466)で算出、休憩は含みません。', 'tours.estimate.basisPace': '距離と入力したペースから算出。', + 'tours.dayTime.workedOut': '{time}に終了(開始時刻とツアー時間{total}から算出)', + 'tours.dayTime.workedOutNextDay': '翌日{time}に終了(開始時刻とツアー時間{total}から算出)', + 'tours.dayTime.overrun': '{end}に終了、{name}の開始{time}より後です', + 'tours.dayTime.nextItem': '次の項目', + 'tours.dayTime.formHint': '空欄のままなら終了時刻を算出します:{time}(開始時刻+ツアー時間{total})。', + 'tours.dayTime.formNoStart': '開始時刻を入れると、ツアー時間{total}から終了時刻を算出します。', }; export default tours; diff --git a/shared/src/i18n/ko/tours.ts b/shared/src/i18n/ko/tours.ts index 603b5c965..b0f5f3f14 100644 --- a/shared/src/i18n/ko/tours.ts +++ b/shared/src/i18n/ko/tours.ts @@ -1268,5 +1268,11 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': '거리, 오르막, 내리막으로 등산로 표지 규칙(DIN 33466)에 따라 계산했으며 휴식은 포함하지 않습니다.', 'tours.estimate.basisPace': '거리와 입력한 페이스로 계산했습니다.', + 'tours.dayTime.workedOut': '{time}에 종료, 시작 시각과 투어 시간 {total}으로 계산', + 'tours.dayTime.workedOutNextDay': '다음 날 {time}에 종료, 시작 시각과 투어 시간 {total}으로 계산', + 'tours.dayTime.overrun': '{end}에 종료, {name}이(가) {time}에 시작한 뒤입니다', + 'tours.dayTime.nextItem': '다음 항목', + 'tours.dayTime.formHint': '비워 두면 종료 시각을 계산합니다: {time}, 시작 시각에 투어 시간 {total}을 더한 값입니다.', + 'tours.dayTime.formNoStart': '시작 시각을 입력하면 투어 시간 {total}으로 종료 시각을 계산합니다.', }; export default tours; diff --git a/shared/src/i18n/nl/tours.ts b/shared/src/i18n/nl/tours.ts index ca08dd44c..d3057064b 100644 --- a/shared/src/i18n/nl/tours.ts +++ b/shared/src/i18n/nl/tours.ts @@ -1333,5 +1333,12 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Berekend uit afstand, stijging en daling volgens de regel van de wandelwegwijzers (DIN 33466), zonder pauzes.', 'tours.estimate.basisPace': 'Berekend uit de afstand bij het ingevoerde tempo.', + 'tours.dayTime.workedOut': 'Eindigt om {time}, berekend uit de start en de tourtijd van {total}', + 'tours.dayTime.workedOutNextDay': + 'Eindigt de volgende dag om {time}, berekend uit de start en de tourtijd van {total}', + 'tours.dayTime.overrun': 'Eindigt om {end}, nadat {name} om {time} begint', + 'tours.dayTime.nextItem': 'het volgende onderdeel', + 'tours.dayTime.formHint': 'Leeg gelaten wordt het einde berekend: {time}, de start plus de tourtijd van {total}.', + 'tours.dayTime.formNoStart': 'Met een starttijd wordt het einde berekend uit de tourtijd van {total}.', }; export default tours; diff --git a/shared/src/i18n/pl/tours.ts b/shared/src/i18n/pl/tours.ts index 5816666ff..616b6a801 100644 --- a/shared/src/i18n/pl/tours.ts +++ b/shared/src/i18n/pl/tours.ts @@ -1366,5 +1366,11 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Wyliczony z dystansu, podejść i zejść według reguły drogowskazów turystycznych (DIN 33466), bez przerw.', 'tours.estimate.basisPace': 'Wyliczony z dystansu przy podanym tempie.', + 'tours.dayTime.workedOut': 'Kończy się o {time}, wyliczone ze startu i czasu trasy {total}', + 'tours.dayTime.workedOutNextDay': 'Kończy się następnego dnia o {time}, wyliczone ze startu i czasu trasy {total}', + 'tours.dayTime.overrun': 'Kończy się o {end}, po rozpoczęciu {name} o {time}', + 'tours.dayTime.nextItem': 'następny punkt', + 'tours.dayTime.formHint': 'Puste pole: koniec zostanie wyliczony, {time}, czyli start plus czas trasy {total}.', + 'tours.dayTime.formNoStart': 'Po podaniu startu koniec zostanie wyliczony z czasu trasy {total}.', }; export default tours; diff --git a/shared/src/i18n/ru/tours.ts b/shared/src/i18n/ru/tours.ts index c701a7099..ad5d9915b 100644 --- a/shared/src/i18n/ru/tours.ts +++ b/shared/src/i18n/ru/tours.ts @@ -1372,5 +1372,12 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Рассчитано по расстоянию, набору и сбросу высоты по правилу туристических указателей (DIN 33466), без перерывов.', 'tours.estimate.basisPace': 'Рассчитано по расстоянию при введённом темпе.', + 'tours.dayTime.workedOut': 'Заканчивается в {time}, рассчитано по началу и времени тура {total}', + 'tours.dayTime.workedOutNextDay': + 'Заканчивается на следующий день в {time}, рассчитано по началу и времени тура {total}', + 'tours.dayTime.overrun': 'Заканчивается в {end}, после начала {name} в {time}', + 'tours.dayTime.nextItem': 'следующий пункт', + 'tours.dayTime.formHint': 'Если оставить пустым, конец рассчитывается: {time}, начало плюс время тура {total}.', + 'tours.dayTime.formNoStart': 'Со временем начала конец рассчитывается по времени тура {total}.', }; export default tours; diff --git a/shared/src/i18n/sk/tours.ts b/shared/src/i18n/sk/tours.ts index 22ed453a1..7f454ddf9 100644 --- a/shared/src/i18n/sk/tours.ts +++ b/shared/src/i18n/sk/tours.ts @@ -1319,5 +1319,11 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Vypočítané zo vzdialenosti, stúpania a klesania podľa pravidla turistických smerovníkov (DIN 33466), bez prestávok.', 'tours.estimate.basisPace': 'Vypočítané zo vzdialenosti pri zadanom tempe.', + 'tours.dayTime.workedOut': 'Končí o {time}, vypočítané zo začiatku a času túry {total}', + 'tours.dayTime.workedOutNextDay': 'Končí na druhý deň o {time}, vypočítané zo začiatku a času túry {total}', + 'tours.dayTime.overrun': 'Končí o {end}, až po začiatku {name} o {time}', + 'tours.dayTime.nextItem': 'ďalšia položka', + 'tours.dayTime.formHint': 'Ak zostane prázdny, koniec sa vypočíta: {time}, začiatok plus čas túry {total}.', + 'tours.dayTime.formNoStart': 'So začiatkom sa koniec vypočíta z času túry {total}.', }; export default tours; diff --git a/shared/src/i18n/sv/tours.ts b/shared/src/i18n/sv/tours.ts index 2acfa6aaf..a4ad4fe14 100644 --- a/shared/src/i18n/sv/tours.ts +++ b/shared/src/i18n/sv/tours.ts @@ -1302,5 +1302,11 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Beräknad från sträcka, stigning och nedstigning enligt vandringsskyltarnas regel (DIN 33466), utan pauser.', 'tours.estimate.basisPace': 'Beräknad från sträckan i angivet tempo.', + 'tours.dayTime.workedOut': 'Slutar {time}, beräknat från starten och turens tid på {total}', + 'tours.dayTime.workedOutNextDay': 'Slutar nästa dag {time}, beräknat från starten och turens tid på {total}', + 'tours.dayTime.overrun': 'Slutar {end}, efter att {name} börjar {time}', + 'tours.dayTime.nextItem': 'nästa punkt', + 'tours.dayTime.formHint': 'Lämnas tomt beräknas slutet: {time}, starten plus turens tid på {total}.', + 'tours.dayTime.formNoStart': 'Med en starttid beräknas slutet från turens tid på {total}.', }; export default tours; diff --git a/shared/src/i18n/th/tours.ts b/shared/src/i18n/th/tours.ts index 736f0fcde..f2b34d114 100644 --- a/shared/src/i18n/th/tours.ts +++ b/shared/src/i18n/th/tours.ts @@ -1246,5 +1246,11 @@ const tours: TranslationStrings = { 'tours.estimate.noHeights': 'เส้นทางไม่มีข้อมูลความสูง จึงประมาณเวลาไม่ได้', 'tours.estimate.basisDin': 'คำนวณจากระยะทาง ระยะขึ้นและลงตามกฎป้ายบอกทางเดินป่า (DIN 33466) ไม่รวมเวลาพัก', 'tours.estimate.basisPace': 'คำนวณจากระยะทางตามเพซที่กรอกไว้', + 'tours.dayTime.workedOut': 'สิ้นสุดเวลา {time} คำนวณจากเวลาเริ่มและเวลาทัวร์ {total}', + 'tours.dayTime.workedOutNextDay': 'สิ้นสุดวันถัดไปเวลา {time} คำนวณจากเวลาเริ่มและเวลาทัวร์ {total}', + 'tours.dayTime.overrun': 'สิ้นสุดเวลา {end} หลังจาก {name} เริ่มเวลา {time}', + 'tours.dayTime.nextItem': 'รายการถัดไป', + 'tours.dayTime.formHint': 'หากเว้นว่างไว้ ระบบจะคำนวณเวลาสิ้นสุด: {time} คือเวลาเริ่มบวกเวลาทัวร์ {total}', + 'tours.dayTime.formNoStart': 'เมื่อมีเวลาเริ่ม ระบบจะคำนวณเวลาสิ้นสุดจากเวลาทัวร์ {total}', }; export default tours; diff --git a/shared/src/i18n/tours-locales.spec.ts b/shared/src/i18n/tours-locales.spec.ts index 6ac0e97d0..4ce8227f6 100644 --- a/shared/src/i18n/tours-locales.spec.ts +++ b/shared/src/i18n/tours-locales.spec.ts @@ -20,7 +20,7 @@ const toursModules = import.meta.glob<{ default: TranslationStrings }>('./*/tour const TOUR_DOMAINS: { file: string; generalKeyCount: number; modules: DomainModules }[] = [ { file: 'tours', - generalKeyCount: 1183, + generalKeyCount: 1189, modules: toursModules, }, { diff --git a/shared/src/i18n/tr/tours.ts b/shared/src/i18n/tr/tours.ts index 72fc35315..1c1cbb125 100644 --- a/shared/src/i18n/tr/tours.ts +++ b/shared/src/i18n/tr/tours.ts @@ -1309,5 +1309,11 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Mesafe, tırmanış ve inişten yürüyüş tabelası kuralına (DIN 33466) göre hesaplandı, molalar hariç.', 'tours.estimate.basisPace': 'Mesafeden, girilen tempoyla hesaplandı.', + 'tours.dayTime.workedOut': '{time} saatinde biter, başlangıç ve {total} tur süresinden hesaplandı', + 'tours.dayTime.workedOutNextDay': 'Ertesi gün {time} saatinde biter, başlangıç ve {total} tur süresinden hesaplandı', + 'tours.dayTime.overrun': '{end} saatinde biter, {name} {time} saatinde başladıktan sonra', + 'tours.dayTime.nextItem': 'sonraki öğe', + 'tours.dayTime.formHint': 'Boş bırakılırsa bitiş hesaplanır: {time}, başlangıç artı {total} tur süresi.', + 'tours.dayTime.formNoStart': 'Bir başlangıç saatiyle bitiş, {total} tur süresinden hesaplanır.', }; export default tours; diff --git a/shared/src/i18n/uk/tours.ts b/shared/src/i18n/uk/tours.ts index 624c43861..358cf7469 100644 --- a/shared/src/i18n/uk/tours.ts +++ b/shared/src/i18n/uk/tours.ts @@ -1358,5 +1358,12 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Розраховано за відстанню, набором і скиданням висоти за правилом туристичних вказівників (DIN 33466), без перерв.', 'tours.estimate.basisPace': 'Розраховано за відстанню при введеному темпі.', + 'tours.dayTime.workedOut': 'Закінчується о {time}, розраховано за початком і часом туру {total}', + 'tours.dayTime.workedOutNextDay': + 'Закінчується наступного дня о {time}, розраховано за початком і часом туру {total}', + 'tours.dayTime.overrun': 'Закінчується о {end}, після початку {name} о {time}', + 'tours.dayTime.nextItem': 'наступний пункт', + 'tours.dayTime.formHint': 'Якщо залишити порожнім, кінець розраховується: {time}, початок плюс час туру {total}.', + 'tours.dayTime.formNoStart': 'З часом початку кінець розраховується за часом туру {total}.', }; export default tours; diff --git a/shared/src/i18n/vi/tours.ts b/shared/src/i18n/vi/tours.ts index 6f051f920..950f62595 100644 --- a/shared/src/i18n/vi/tours.ts +++ b/shared/src/i18n/vi/tours.ts @@ -1291,5 +1291,11 @@ const tours: TranslationStrings = { 'tours.estimate.basisDin': 'Tính từ quãng đường, độ cao lên và xuống theo quy tắc biển chỉ dẫn leo núi (DIN 33466), không tính nghỉ.', 'tours.estimate.basisPace': 'Tính từ quãng đường theo tốc độ đã nhập.', + 'tours.dayTime.workedOut': 'Kết thúc lúc {time}, tính từ giờ bắt đầu và thời gian tour {total}', + 'tours.dayTime.workedOutNextDay': 'Kết thúc lúc {time} ngày hôm sau, tính từ giờ bắt đầu và thời gian tour {total}', + 'tours.dayTime.overrun': 'Kết thúc lúc {end}, sau khi {name} bắt đầu lúc {time}', + 'tours.dayTime.nextItem': 'mục tiếp theo', + 'tours.dayTime.formHint': 'Để trống thì giờ kết thúc được tính: {time}, giờ bắt đầu cộng thời gian tour {total}.', + 'tours.dayTime.formNoStart': 'Khi có giờ bắt đầu, giờ kết thúc được tính từ thời gian tour {total}.', }; export default tours; diff --git a/shared/src/i18n/zh-TW/tours.ts b/shared/src/i18n/zh-TW/tours.ts index 039448b33..9d1c3770f 100644 --- a/shared/src/i18n/zh-TW/tours.ts +++ b/shared/src/i18n/zh-TW/tours.ts @@ -1209,5 +1209,11 @@ const tours: TranslationStrings = { 'tours.estimate.noHeights': '軌跡沒有海拔資料,無法估算時間。', 'tours.estimate.basisDin': '依健行路標規則(DIN 33466)由距離、爬升與下降計算,不含休息。', 'tours.estimate.basisPace': '依距離與輸入的配速計算。', + 'tours.dayTime.workedOut': '{time} 結束,依開始時間與行程時間 {total} 推算', + 'tours.dayTime.workedOutNextDay': '隔天 {time} 結束,依開始時間與行程時間 {total} 推算', + 'tours.dayTime.overrun': '{end} 結束,晚於 {name} 的開始時間 {time}', + 'tours.dayTime.nextItem': '下一項', + 'tours.dayTime.formHint': '留空時會推算結束時間:{time},即開始時間加上行程時間 {total}。', + 'tours.dayTime.formNoStart': '填入開始時間後,會依行程時間 {total} 推算結束時間。', }; export default tours; diff --git a/shared/src/i18n/zh/tours.ts b/shared/src/i18n/zh/tours.ts index e04470bb6..a89417981 100644 --- a/shared/src/i18n/zh/tours.ts +++ b/shared/src/i18n/zh/tours.ts @@ -1210,5 +1210,11 @@ const tours: TranslationStrings = { 'tours.estimate.noHeights': '轨迹没有海拔数据,无法估算用时。', 'tours.estimate.basisDin': '按徒步路标规则(DIN 33466)根据距离、爬升和下降计算,不含休息。', 'tours.estimate.basisPace': '按距离和输入的配速计算。', + 'tours.dayTime.workedOut': '{time} 结束,根据开始时间和行程用时 {total} 推算', + 'tours.dayTime.workedOutNextDay': '次日 {time} 结束,根据开始时间和行程用时 {total} 推算', + 'tours.dayTime.overrun': '{end} 结束,晚于 {name} 的开始时间 {time}', + 'tours.dayTime.nextItem': '下一项', + 'tours.dayTime.formHint': '留空时会推算结束时间:{time},即开始时间加上行程用时 {total}。', + 'tours.dayTime.formNoStart': '填写开始时间后,会根据行程用时 {total} 推算结束时间。', }; export default tours;