From 290961cced794e91e8a20a9543d4b50b7dfa00fd Mon Sep 17 00:00:00 2001 From: Maurice Date: Mon, 19 Oct 2026 22:38:03 +0200 Subject: [PATCH] feat(tours): check a city walk's stops off for the day it is on (CW-03) The walk's detail opened from a day lists its stops in order, each with its stay, importance and protection, and marks one visited or skipped for that day, or takes it back. The day row shows how many were visited. Marks reach the other travellers at once and change no route, place or order; a copy or another day starts with none. --- client/src/api/tourVisits.ts | 17 ++ client/src/api/wsEventPolicy.ts | 2 + .../Planner/DayPlanPlaceSubline.tsx | 6 +- .../src/components/Planner/DayPlanSidebar.tsx | 2 +- .../src/components/Tours/TourDetailDialog.tsx | 4 + .../components/Tours/walk/WalkStations.tsx | 180 ++++++++++++++++++ .../Tours/walk/useTourVisitsSync.ts | 39 ++++ .../src/pages/tripPlanner/useTripPlanner.ts | 3 + client/src/repo/tourVisitRepo.ts | 24 +++ client/src/store/tourVisitStore.ts | 68 +++++++ client/tests/helpers/storeRegistry.ts | 1 + shared/src/i18n/ar/tours.ts | 10 + shared/src/i18n/az/tours.ts | 10 + shared/src/i18n/br/tours.ts | 10 + shared/src/i18n/ca/tours.ts | 10 + shared/src/i18n/cs/tours.ts | 10 + shared/src/i18n/de/tours.ts | 11 ++ shared/src/i18n/en/tours.ts | 10 + shared/src/i18n/es/tours.ts | 10 + shared/src/i18n/et/tours.ts | 10 + shared/src/i18n/fr/tours.ts | 10 + shared/src/i18n/gr/tours.ts | 10 + shared/src/i18n/hu/tours.ts | 10 + shared/src/i18n/id/tours.ts | 10 + shared/src/i18n/it/tours.ts | 10 + shared/src/i18n/ja/tours.ts | 11 ++ shared/src/i18n/ko/tours.ts | 10 + shared/src/i18n/nl/tours.ts | 11 ++ shared/src/i18n/pl/tours.ts | 10 + shared/src/i18n/ru/tours.ts | 10 + shared/src/i18n/sk/tours.ts | 10 + shared/src/i18n/sv/tours.ts | 10 + shared/src/i18n/th/tours.ts | 10 + shared/src/i18n/tours-locales.spec.ts | 2 +- shared/src/i18n/tr/tours.ts | 10 + shared/src/i18n/uk/tours.ts | 10 + shared/src/i18n/vi/tours.ts | 10 + shared/src/i18n/zh-TW/tours.ts | 10 + shared/src/i18n/zh/tours.ts | 10 + 39 files changed, 618 insertions(+), 3 deletions(-) create mode 100644 client/src/api/tourVisits.ts create mode 100644 client/src/components/Tours/walk/WalkStations.tsx create mode 100644 client/src/components/Tours/walk/useTourVisitsSync.ts create mode 100644 client/src/repo/tourVisitRepo.ts create mode 100644 client/src/store/tourVisitStore.ts diff --git a/client/src/api/tourVisits.ts b/client/src/api/tourVisits.ts new file mode 100644 index 000000000..550204873 --- /dev/null +++ b/client/src/api/tourVisits.ts @@ -0,0 +1,17 @@ +import type { TourVisitListResponse, TourVisitUpdateRequest } from '@trek/shared'; +import apiClient from './client'; + +/** CW-03: the stations of a trip's city walks visited or skipped on their days. */ +export const tourVisitsApi = { + list: (tripId: number | string): Promise => + apiClient.get(`/trips/${tripId}/tour-visits`).then((r) => r.data), + update: ( + tripId: number | string, + assignmentId: number, + stationKey: string, + body: TourVisitUpdateRequest + ): Promise => + apiClient + .put(`/trips/${tripId}/tour-visits/${assignmentId}/${encodeURIComponent(stationKey)}`, body) + .then((r) => r.data), +}; diff --git a/client/src/api/wsEventPolicy.ts b/client/src/api/wsEventPolicy.ts index b7c510062..0d016abac 100644 --- a/client/src/api/wsEventPolicy.ts +++ b/client/src/api/wsEventPolicy.ts @@ -19,6 +19,8 @@ export const HANDLED_OUTSIDE_TRIP_STORE = [ // Tours facet invalidation — hooks/useTripWebSocket forwards tours:changed to // useTourPlaceIds so the active trip refreshes its Tours/Places read model. 'tours:changed', + // CW-03 — components/Tours/walk/useTourVisitsSync keeps the visits store current. + 'tours:visits', // Collab — Collab/MCollab components + useTripWebSocket's collabFileSync 'collab:note:created', 'collab:note:updated', diff --git a/client/src/components/Planner/DayPlanPlaceSubline.tsx b/client/src/components/Planner/DayPlanPlaceSubline.tsx index a7cb79505..8d40d4cb1 100644 --- a/client/src/components/Planner/DayPlanPlaceSubline.tsx +++ b/client/src/components/Planner/DayPlanPlaceSubline.tsx @@ -7,6 +7,7 @@ import type { Category, Place } from '../../types'; import { formatTime } from '../../utils/formatters'; import { fs } from '../shared/DialogShell'; import { TourDayRowPills } from '../Tours/TourDayRowFacts'; +import { WalkProgressPill } from '../Tours/walk/WalkStations'; import { movingTimeLabel } from '../Tours/tourFacts'; import { formatPlannedTourDuration, type TourPlannedTimes } from '../Tours/tourPresentation'; import { TimePill } from './planParts'; @@ -19,6 +20,8 @@ interface DayPlanPlaceSublineProps { tourTimes: DayRowTourTimes | undefined; isTourPlace: boolean; timeFormat: string; + /** CW-03: the row's day assignment, which a city walk's progress belongs to. */ + assignmentId?: number; } function hasPlannedTimes(tourTimes: DayRowTourTimes | undefined): boolean { @@ -90,7 +93,7 @@ function SublineDescription({ text, isTourPlace }: { text: string; isTourPlace: // Second line of a place row in the day plan: time, tour facts or planned // tour durations, then the clamped description. -export function DayPlanPlaceSubline({ place, cat, tourTimes, isTourPlace, timeFormat }: DayPlanPlaceSublineProps) { +export function DayPlanPlaceSubline({ place, cat, tourTimes, isTourPlace, timeFormat, assignmentId }: DayPlanPlaceSublineProps) { const { locale } = useTranslation(); const dayRowTour = tourTimes?.tour; const description = place.description || place.address || cat?.name; @@ -106,6 +109,7 @@ export function DayPlanPlaceSubline({ place, cat, tourTimes, isTourPlace, timeFo )} {dayRowTour ? : tourTimes && } + {dayRowTour && } {description && } ); diff --git a/client/src/components/Planner/DayPlanSidebar.tsx b/client/src/components/Planner/DayPlanSidebar.tsx index 8d8ecf710..5f817d295 100644 --- a/client/src/components/Planner/DayPlanSidebar.tsx +++ b/client/src/components/Planner/DayPlanSidebar.tsx @@ -2033,7 +2033,7 @@ const DayPlanSidebar = React.memo(function DayPlanSidebar(props: DayPlanSidebarP )} - + {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/Tours/TourDetailDialog.tsx b/client/src/components/Tours/TourDetailDialog.tsx index 6f578a81d..a4f8261e5 100644 --- a/client/src/components/Tours/TourDetailDialog.tsx +++ b/client/src/components/Tours/TourDetailDialog.tsx @@ -64,6 +64,7 @@ import { } from './tourPresentation'; import { useTourDayAssignment } from './useTourDayAssignment'; import { useTourPermissions, type TourPermissionProps } from './useTourPermissions'; +import { WalkStationsCard } from './walk/WalkStations'; interface TourDetailDialogProps extends TourPermissionProps { tour: TourListItem; @@ -652,6 +653,9 @@ export default function TourDetailDialog({ placeId={place.id} /> )} + {tour.tour_type === 'walk' && ( + + )} {tour.tour_type === 'kayak' && } {tour.tour_type === 'kayak' && } diff --git a/client/src/components/Tours/walk/WalkStations.tsx b/client/src/components/Tours/walk/WalkStations.tsx new file mode 100644 index 000000000..e2233fc66 --- /dev/null +++ b/client/src/components/Tours/walk/WalkStations.tsx @@ -0,0 +1,180 @@ +import type { TourListItem, TourVisitStatus } from '@trek/shared'; +import { Check, CheckCheck, Redo2, SkipForward, Undo2 } from 'lucide-react'; +import { useEffect, useState } from 'react'; +import { useTranslation } from '../../../i18n'; +import { tourRepo } from '../../../repo/tourRepo'; +import { useTourVisitStore } from '../../../store/tourVisitStore'; +import { SoftPill } from '../../Planner/planParts'; +import { DialogSection, fs } from '../../shared/DialogShell'; +import { useToast } from '../../shared/Toast'; +import { StationPills } from '../planner/stationParts'; +import { plannerWaypointsOf, type TourPlannerWaypoint } from '../planner/tourPlannerWaypoints'; +import { TourDayRowPill } from '../TourDayRowFacts'; +import { CardAction } from '../tourParts'; + +let nextId = 0; +const freshId = () => `station-${++nextId}`; + +/** CW-03: a saved walk's stations in order, read once for the dialog; null while they load or could not be read. */ +function useWalkStations(tripId: number | string, placeId: number): TourPlannerWaypoint[] | null { + const [stations, setStations] = useState<{ placeId: number; points: TourPlannerWaypoint[] } | null>(null); + useEffect(() => { + const controller = new AbortController(); + tourRepo + .detail(tripId, placeId, controller.signal) + .then((detail) => { + if (controller.signal.aborted) return; + const points = plannerWaypointsOf(detail.waypoints, freshId).filter((point) => !point.shaping); + setStations({ placeId, points }); + }) + .catch(() => { + if (!controller.signal.aborted) setStations({ placeId, points: [] }); + }); + return () => controller.abort(); + }, [placeId, tripId]); + return stations?.placeId === placeId ? stations.points : null; +} + +/** The day's status of each station, by its key. */ +function useDayVisits(assignmentId: number | null) { + return useTourVisitStore((state) => (assignmentId === null ? undefined : state.byAssignment[assignmentId])); +} + +/** + * CW-03: a walk's progress on one of its days, as a pill of the day row: + * how many of its stations were visited. Nothing is shown before any is. + */ +export function WalkProgressPill({ tour, assignmentId }: { tour: TourListItem; assignmentId: number | undefined }) { + const { t } = useTranslation(); + const visits = useDayVisits(assignmentId ?? null); + const total = tour.station_count ?? 0; + if (tour.tour_type !== 'walk' || total === 0 || !visits) return null; + const visited = Object.values(visits).filter((status) => status === 'visited').length; + if (visited === 0 && Object.keys(visits).length === 0) return null; + const label = t('tours.walk.progress', { visited, total }); + return ; +} + +function StatusActions({ + status, + onMark, +}: { + status: TourVisitStatus | undefined; + onMark: (status: TourVisitStatus | 'planned') => void; +}) { + const { t } = useTranslation(); + if (status) + return ( + } onClick={() => onMark('planned')}> + {t('tours.walk.visit.undo')} + + ); + return ( + + } onClick={() => onMark('visited')}> + {t('tours.walk.visit.visited')} + + } onClick={() => onMark('skipped')}> + {t('tours.walk.visit.skip')} + + + ); +} + +/** + * CW-03: a city walk's stations in its detail, in order, each with its stay, + * importance and protection; opened from a day, each can be marked visited or + * skipped for that day and taken back. Marking changes no route, place or order. + */ +export function WalkStationsCard({ + tour, + tripId, + assignmentId, + canMark, +}: { + tour: TourListItem; + tripId: number | string; + /** The day the dialog was opened from; without one there is nothing to check off. */ + assignmentId: number | null; + canMark: boolean; +}) { + const { t } = useTranslation(); + const toast = useToast(); + const stations = useWalkStations(tripId, tour.place_id); + const visits = useDayVisits(assignmentId); + const mark = useTourVisitStore((state) => state.mark); + if (tour.tour_type !== 'walk') return null; + const onMark = (key: string, status: TourVisitStatus | 'planned') => { + if (assignmentId === null) return; + mark(tripId, assignmentId, key, status).catch(() => toast.error(t('tours.walk.visit.failed'))); + }; + const visited = stations + ? stations.filter((station) => station.key && visits?.[station.key] === 'visited').length + : 0; + return ( + {t('tours.walk.progress', { visited, total: stations.length })} + ) : undefined + } + > +
+ {stations === null && ( + + {t('common.loading')} + + )} + {assignmentId === null && stations && stations.length > 0 && ( + + {t('tours.walk.visit.fromDay')} + + )} +
    + {(stations ?? []).map((station, index) => { + const status = station.key ? visits?.[station.key] : undefined; + return ( +
  1. + + {status === 'visited' ? ( + + ) : status === 'skipped' ? ( + + ) : ( + index + 1 + )} + + + + {station.name ?? t('tours.walk.stationNumbered', { number: index + 1 })} + + + {status && ( + + {t(status === 'visited' ? 'tours.walk.visit.isVisited' : 'tours.walk.visit.isSkipped')} + + )} + + {canMark && assignmentId !== null && station.key && ( + onMark(station.key ?? '', next)} /> + )} +
  2. + ); + })} +
+
+
+ ); +} diff --git a/client/src/components/Tours/walk/useTourVisitsSync.ts b/client/src/components/Tours/walk/useTourVisitsSync.ts new file mode 100644 index 000000000..ec9185466 --- /dev/null +++ b/client/src/components/Tours/walk/useTourVisitsSync.ts @@ -0,0 +1,39 @@ +import { tourVisitSchema } from '@trek/shared'; +import { useEffect } from 'react'; +import { addListener, removeListener } from '../../../api/websocket'; +import { useTourVisitStore } from '../../../store/tourVisitStore'; +import type { WebSocketEvent } from '../../../types'; + +/** + * CW-03: the visits of a trip's city walks, read when the trip opens and kept + * current by its socket, so a station another traveller checks off shows on + * every day plan at once. Offline the day plan shows none until it is back. + */ +export function useTourVisitsSync(tripId: number | string | undefined, enabled: boolean) { + useEffect(() => { + if (!enabled || tripId === undefined) return; + let current = true; + useTourVisitStore + .getState() + .load(tripId) + .catch(() => { + // Offline or refused, the day plan shows no progress rather than another trip's. + if (current) useTourVisitStore.getState().reset(tripId); + }); + const onEvent = (event: WebSocketEvent) => { + if (event?.type !== 'tours:visits' || String(event.tripId ?? '') !== String(tripId)) return; + const visits = Array.isArray(event.visits) + ? event.visits.flatMap((raw) => { + const parsed = tourVisitSchema.safeParse(raw); + return parsed.success ? [parsed.data] : []; + }) + : []; + useTourVisitStore.getState().applyRemote(Number(event.assignmentId), visits); + }; + addListener(onEvent); + return () => { + current = false; + removeListener(onEvent); + }; + }, [enabled, tripId]); +} diff --git a/client/src/pages/tripPlanner/useTripPlanner.ts b/client/src/pages/tripPlanner/useTripPlanner.ts index 3a0803a7b..030155549 100644 --- a/client/src/pages/tripPlanner/useTripPlanner.ts +++ b/client/src/pages/tripPlanner/useTripPlanner.ts @@ -1,3 +1,4 @@ +import { useTourVisitsSync } from '../../components/Tours/walk/useTourVisitsSync' import { useRoadtripSettings } from '../../hooks/useRoadtripSettings' import { useState, useEffect, useCallback, useMemo, useRef } from 'react' import { useParams, useNavigate, useSearchParams } from 'react-router' @@ -284,6 +285,8 @@ export function useTripPlanner() { const [toursMode, setToursMode] = useState(false) const previousToursModeRef = useRef(false) const { tours, toursLoading, tourDataReady, tourPlaceIds, reloadTourPlaceIds, invalidateTourPlaceIds, upsertTour } = useTourPlaceIds(tripId, toursEnabled) + // CW-03: how far the trip's city walks got on their days, for the day plan and the walks' stations. + useTourVisitsSync(tripId, toursEnabled) useEffect(() => { if (!toursEnabled) { diff --git a/client/src/repo/tourVisitRepo.ts b/client/src/repo/tourVisitRepo.ts new file mode 100644 index 000000000..368b316c7 --- /dev/null +++ b/client/src/repo/tourVisitRepo.ts @@ -0,0 +1,24 @@ +import type { TourVisitListResponse, TourVisitUpdateRequest } from '@trek/shared'; +import { tourVisitsApi } from '../api/tourVisits'; +import { requireOnline } from './onlineOnly'; + +/** + * CW-03, online only for now: a station checked off is the server's at once, + * so everyone on the trip sees the same day. Offline the station says so and + * waits; the client keeps no copy of its own. + */ +export const tourVisitRepo = { + async list(tripId: number | string): Promise { + requireOnline(); + return await tourVisitsApi.list(tripId); + }, + async update( + tripId: number | string, + assignmentId: number, + stationKey: string, + body: TourVisitUpdateRequest + ): Promise { + requireOnline(); + return await tourVisitsApi.update(tripId, assignmentId, stationKey, body); + }, +}; diff --git a/client/src/store/tourVisitStore.ts b/client/src/store/tourVisitStore.ts new file mode 100644 index 000000000..12d069ba7 --- /dev/null +++ b/client/src/store/tourVisitStore.ts @@ -0,0 +1,68 @@ +import type { TourVisit, TourVisitStatus } from '@trek/shared'; +import { create } from 'zustand'; +import { tourVisitRepo } from '../repo/tourVisitRepo'; + +/** A station's status on one day of its walk; a station not listed is planned. */ +type VisitsByStation = Readonly>; + +interface TourVisitState { + /** The trip the visits were read for. */ + tripId: string | null; + /** Per day assignment, the stations visited or skipped. */ + byAssignment: Readonly>; + load: (tripId: number | string) => Promise; + /** Nothing known for this trip: its visits could not be read. */ + reset: (tripId: number | string) => void; + /** Marks a station, at once on screen; a write the server refuses puts it back and throws. */ + mark: ( + tripId: number | string, + assignmentId: number, + stationKey: string, + status: TourVisitStatus | 'planned' + ) => Promise; + /** Another traveller marked a station: the day's visits as the server has them now. */ + applyRemote: (assignmentId: number, visits: readonly TourVisit[]) => void; +} + +function byStation(visits: readonly TourVisit[]): VisitsByStation { + return Object.fromEntries(visits.map((visit) => [visit.station_key, visit.status])); +} + +function withStatus(day: VisitsByStation | undefined, key: string, status: TourVisitStatus | 'planned') { + const next = { ...day }; + if (status === 'planned') delete next[key]; + else next[key] = status; + return next; +} + +/** + * CW-03: how far each city walk got on its days, for the day plan's rows and + * the walk's stations. Read once a trip opens, kept current by the trip's + * socket, and written one station at a time. + */ +export const useTourVisitStore = create((set, get) => ({ + tripId: null, + byAssignment: {}, + load: async (tripId) => { + const { visits } = await tourVisitRepo.list(tripId); + const grouped: Record> = {}; + for (const visit of visits) (grouped[visit.assignment_id] ??= {})[visit.station_key] = visit.status; + set({ tripId: String(tripId), byAssignment: grouped }); + }, + reset: (tripId) => set({ tripId: String(tripId), byAssignment: {} }), + mark: async (tripId, assignmentId, stationKey, status) => { + const before = get().byAssignment[assignmentId]; + set((state) => ({ + byAssignment: { ...state.byAssignment, [assignmentId]: withStatus(before, stationKey, status) }, + })); + try { + const { visits } = await tourVisitRepo.update(tripId, assignmentId, stationKey, { status }); + get().applyRemote(assignmentId, visits); + } catch (failure) { + set((state) => ({ byAssignment: { ...state.byAssignment, [assignmentId]: before ?? {} } })); + throw failure; + } + }, + applyRemote: (assignmentId, visits) => + set((state) => ({ byAssignment: { ...state.byAssignment, [assignmentId]: byStation(visits) } })), +})); diff --git a/client/tests/helpers/storeRegistry.ts b/client/tests/helpers/storeRegistry.ts index e1950889d..06ffdf946 100644 --- a/client/tests/helpers/storeRegistry.ts +++ b/client/tests/helpers/storeRegistry.ts @@ -62,5 +62,6 @@ export const UNRESET_STORES: Record = { useTourPoiStore: 'the places found along a tour; the tests that read it clear it in their own beforeEach', useTourRouteHighlightStore: 'the stretches a bike card points at; the tests that read it clear it in their own beforeEach', + useTourVisitStore: 'the visits of city walks per day; the tests that read it reset it in their own beforeEach', useTripJournalLinkStore: 'the journal link per trip; the tests that read it reset it in their own beforeEach', }; diff --git a/shared/src/i18n/ar/tours.ts b/shared/src/i18n/ar/tours.ts index bda6de518..28ad8910b 100644 --- a/shared/src/i18n/ar/tours.ts +++ b/shared/src/i18n/ar/tours.ts @@ -499,6 +499,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'وقت المسار سيرًا بين المحطات، دون مدد البقاء.', 'tours.walk.stays': 'مدد البقاء', 'tours.walk.totalAtLeast': '{time} على الأقل', + 'tours.walk.stations': 'محطات الجولة', + 'tours.walk.stationNumbered': 'المحطة {number}', + 'tours.walk.progress': 'تمت زيارة {visited} من {total}', + 'tours.walk.visit.visited': 'تمت الزيارة', + 'tours.walk.visit.skip': 'تخطي', + 'tours.walk.visit.undo': 'تراجع', + 'tours.walk.visit.isVisited': 'تمت زيارتها في هذا اليوم', + 'tours.walk.visit.isSkipped': 'تم تخطيها في هذا اليوم', + 'tours.walk.visit.fromDay': 'افتح الجولة من يوم في الخطة لتأشير محطاتها لذلك اليوم.', + 'tours.walk.visit.failed': 'تعذر تأشير المحطة. يتطلب ذلك اتصالًا؛ حاول مجددًا بعد قليل.', 'tours.planner.tourType': 'نوع الجولة', 'tours.paddle.boat': 'القارب', 'tours.paddle.boat.kayak': 'كاياك', diff --git a/shared/src/i18n/az/tours.ts b/shared/src/i18n/az/tours.ts index be3526668..17986d22f 100644 --- a/shared/src/i18n/az/tours.ts +++ b/shared/src/i18n/az/tours.ts @@ -494,6 +494,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'Dayanacaqlar arasında marşrutlaşdırıcının piyada vaxtı, qalma müddətləri olmadan.', 'tours.walk.stays': 'Qalmalar', 'tours.walk.totalAtLeast': 'ən azı {time}', + 'tours.walk.stations': 'Gəzintinin dayanacaqları', + 'tours.walk.stationNumbered': 'Dayanacaq {number}', + 'tours.walk.progress': '{total} dayanacaqdan {visited} ziyarət edildi', + 'tours.walk.visit.visited': 'Ziyarət edildi', + 'tours.walk.visit.skip': 'Keç', + 'tours.walk.visit.undo': 'Geri al', + 'tours.walk.visit.isVisited': 'Bu gün ziyarət edildi', + 'tours.walk.visit.isSkipped': 'Bu gün keçildi', + 'tours.walk.visit.fromDay': 'Dayanacaqları o gün üçün qeyd etmək üçün gəzintini plandakı bir gündən aç.', + 'tours.walk.visit.failed': 'Dayanacaq qeyd edilə bilmədi. Bunun üçün bağlantı lazımdır; bir azdan yenidən cəhd et.', 'tours.planner.tourType': 'Tur növü', 'tours.paddle.boat': 'Qayıq', 'tours.paddle.boat.kayak': 'Kayak', // same-as-en diff --git a/shared/src/i18n/br/tours.ts b/shared/src/i18n/br/tours.ts index 3ffb4b878..8d0b32ab6 100644 --- a/shared/src/i18n/br/tours.ts +++ b/shared/src/i18n/br/tours.ts @@ -500,6 +500,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'O tempo a pé do roteador entre as paradas, sem as permanências.', 'tours.walk.stays': 'Permanências', 'tours.walk.totalAtLeast': 'pelo menos {time}', + 'tours.walk.stations': 'Paradas do passeio', + 'tours.walk.stationNumbered': 'Parada {number}', + 'tours.walk.progress': '{visited} de {total} visitadas', + 'tours.walk.visit.visited': 'Visitada', + 'tours.walk.visit.skip': 'Pular', + 'tours.walk.visit.undo': 'Desfazer', + 'tours.walk.visit.isVisited': 'Visitada neste dia', + 'tours.walk.visit.isSkipped': 'Pulada neste dia', + 'tours.walk.visit.fromDay': 'Abra o passeio a partir de um dia do plano para marcar as paradas daquele dia.', + 'tours.walk.visit.failed': 'Não foi possível marcar a parada. É preciso conexão; tente de novo em instantes.', 'tours.planner.tourType': 'Tipo de passeio', 'tours.paddle.boat': 'Embarcação', 'tours.paddle.boat.kayak': 'Caiaque', diff --git a/shared/src/i18n/ca/tours.ts b/shared/src/i18n/ca/tours.ts index b9d44b449..c39b15e5a 100644 --- a/shared/src/i18n/ca/tours.ts +++ b/shared/src/i18n/ca/tours.ts @@ -501,6 +501,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'El temps a peu del càlcul de ruta entre parades, sense les estades.', 'tours.walk.stays': 'Estades', 'tours.walk.totalAtLeast': 'com a mínim {time}', + 'tours.walk.stations': 'Parades de la ruta', + 'tours.walk.stationNumbered': 'Parada {number}', + 'tours.walk.progress': '{visited} de {total} visitades', + 'tours.walk.visit.visited': 'Visitada', + 'tours.walk.visit.skip': 'Omet', + 'tours.walk.visit.undo': 'Desfés', + 'tours.walk.visit.isVisited': 'Visitada aquest dia', + 'tours.walk.visit.isSkipped': 'Omesa aquest dia', + 'tours.walk.visit.fromDay': "Obre la ruta des d'un dia del pla per marcar-ne les parades d'aquell dia.", + 'tours.walk.visit.failed': "No s'ha pogut marcar la parada. Cal connexió; torna-ho a provar d'aquí a una estona.", 'tours.planner.tourType': 'Tipus de ruta', 'tours.paddle.boat': 'Embarcació', 'tours.paddle.boat.kayak': 'Caiac', diff --git a/shared/src/i18n/cs/tours.ts b/shared/src/i18n/cs/tours.ts index 32d0ff989..d0272812a 100644 --- a/shared/src/i18n/cs/tours.ts +++ b/shared/src/i18n/cs/tours.ts @@ -491,6 +491,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'Doba chůze mezi zastávkami podle plánovače trasy, bez pobytů.', 'tours.walk.stays': 'Pobyty', 'tours.walk.totalAtLeast': 'nejméně {time}', + 'tours.walk.stations': 'Zastávky procházky', + 'tours.walk.stationNumbered': 'Zastávka {number}', + 'tours.walk.progress': 'Navštíveno {visited} z {total}', + 'tours.walk.visit.visited': 'Navštíveno', + 'tours.walk.visit.skip': 'Přeskočit', + 'tours.walk.visit.undo': 'Vrátit', + 'tours.walk.visit.isVisited': 'Navštíveno v tento den', + 'tours.walk.visit.isSkipped': 'Přeskočeno v tento den', + 'tours.walk.visit.fromDay': 'Otevři procházku ze dne v plánu, abys mohl odškrtat zastávky pro ten den.', + 'tours.walk.visit.failed': 'Zastávku se nepodařilo označit. Je potřeba připojení; zkus to za chvíli znovu.', 'tours.planner.tourType': 'Typ túry', 'tours.paddle.boat': 'Loď', 'tours.paddle.boat.kayak': 'Kajak', diff --git a/shared/src/i18n/de/tours.ts b/shared/src/i18n/de/tours.ts index d4fd96c74..8feab6c8b 100644 --- a/shared/src/i18n/de/tours.ts +++ b/shared/src/i18n/de/tours.ts @@ -505,6 +505,17 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'Die Zeit des Routers zu Fuß zwischen den Stationen, ohne die Aufenthalte.', 'tours.walk.stays': 'Aufenthalte', 'tours.walk.totalAtLeast': 'mindestens {time}', + 'tours.walk.stations': 'Stationen', + 'tours.walk.stationNumbered': 'Station {number}', + 'tours.walk.progress': '{visited} von {total} besucht', + 'tours.walk.visit.visited': 'Besucht', + 'tours.walk.visit.skip': 'Überspringen', + 'tours.walk.visit.undo': 'Zurücknehmen', + 'tours.walk.visit.isVisited': 'An diesem Tag besucht', + 'tours.walk.visit.isSkipped': 'An diesem Tag übersprungen', + 'tours.walk.visit.fromDay': 'Öffne den Walk über einen Tag im Plan, um seine Stationen für diesen Tag abzuhaken.', + 'tours.walk.visit.failed': + 'Die Station konnte nicht markiert werden. Dafür braucht es eine Verbindung; versuch es gleich noch einmal.', 'tours.planner.tourType': 'Tourtyp', 'tours.paddle.boat': 'Boot', 'tours.paddle.boat.kayak': 'Kajak', diff --git a/shared/src/i18n/en/tours.ts b/shared/src/i18n/en/tours.ts index 3d371576a..cd202eaff 100644 --- a/shared/src/i18n/en/tours.ts +++ b/shared/src/i18n/en/tours.ts @@ -492,6 +492,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': "The router's time on foot between the stops, without the stays.", 'tours.walk.stays': 'Stays', 'tours.walk.totalAtLeast': 'at least {time}', + 'tours.walk.stations': 'Stops of the walk', + 'tours.walk.stationNumbered': 'Stop {number}', + 'tours.walk.progress': '{visited} of {total} visited', + 'tours.walk.visit.visited': 'Mark visited', + 'tours.walk.visit.skip': 'Skip', + 'tours.walk.visit.undo': 'Take back', + 'tours.walk.visit.isVisited': 'Visited on this day', + 'tours.walk.visit.isSkipped': 'Skipped on this day', + 'tours.walk.visit.fromDay': 'Open the walk from a day of the plan to check its stops off for that day.', + 'tours.walk.visit.failed': 'The stop could not be marked. It needs a connection; try again in a moment.', 'tours.planner.tourType': 'Tour type', 'tours.paddle.boat': 'Boat', 'tours.paddle.boat.kayak': 'Kayak', diff --git a/shared/src/i18n/es/tours.ts b/shared/src/i18n/es/tours.ts index 5cf6385cf..90971ffde 100644 --- a/shared/src/i18n/es/tours.ts +++ b/shared/src/i18n/es/tours.ts @@ -499,6 +499,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'El tiempo a pie del planificador entre paradas, sin las estancias.', 'tours.walk.stays': 'Estancias', 'tours.walk.totalAtLeast': 'al menos {time}', + 'tours.walk.stations': 'Paradas del paseo', + 'tours.walk.stationNumbered': 'Parada {number}', + 'tours.walk.progress': '{visited} de {total} visitadas', + 'tours.walk.visit.visited': 'Visitada', + 'tours.walk.visit.skip': 'Omitir', + 'tours.walk.visit.undo': 'Deshacer', + 'tours.walk.visit.isVisited': 'Visitada este día', + 'tours.walk.visit.isSkipped': 'Omitida este día', + 'tours.walk.visit.fromDay': 'Abre el paseo desde un día del plan para marcar sus paradas de ese día.', + 'tours.walk.visit.failed': 'No se pudo marcar la parada. Hace falta conexión; inténtalo de nuevo en un momento.', 'tours.planner.tourType': 'Tipo de ruta', 'tours.paddle.boat': 'Embarcación', 'tours.paddle.boat.kayak': 'Kayak', // same-as-en diff --git a/shared/src/i18n/et/tours.ts b/shared/src/i18n/et/tours.ts index ba96203e0..16d61ad36 100644 --- a/shared/src/i18n/et/tours.ts +++ b/shared/src/i18n/et/tours.ts @@ -494,6 +494,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'Marsruutija kõndimisaeg peatuste vahel, ilma peatumisteta.', 'tours.walk.stays': 'Peatumised', 'tours.walk.totalAtLeast': 'vähemalt {time}', + 'tours.walk.stations': 'Jalutuse peatused', + 'tours.walk.stationNumbered': 'Peatus {number}', + 'tours.walk.progress': '{visited} / {total} külastatud', + 'tours.walk.visit.visited': 'Külastatud', + 'tours.walk.visit.skip': 'Jäta vahele', + 'tours.walk.visit.undo': 'Võta tagasi', + 'tours.walk.visit.isVisited': 'Sel päeval külastatud', + 'tours.walk.visit.isSkipped': 'Sel päeval vahele jäetud', + 'tours.walk.visit.fromDay': 'Ava jalutus plaani päevast, et selle päeva peatused ära märkida.', + 'tours.walk.visit.failed': 'Peatust ei õnnestunud märkida. Selleks on vaja ühendust; proovi kohe uuesti.', 'tours.planner.tourType': 'Matka tüüp', 'tours.paddle.boat': 'Paat', 'tours.paddle.boat.kayak': 'Süst', diff --git a/shared/src/i18n/fr/tours.ts b/shared/src/i18n/fr/tours.ts index d314b0b3d..d0faf1df3 100644 --- a/shared/src/i18n/fr/tours.ts +++ b/shared/src/i18n/fr/tours.ts @@ -509,6 +509,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': "Le temps à pied du calcul d'itinéraire entre les étapes, sans les arrêts.", 'tours.walk.stays': 'Arrêts', 'tours.walk.totalAtLeast': 'au moins {time}', + 'tours.walk.stations': 'Étapes de la balade', + 'tours.walk.stationNumbered': 'Étape {number}', + 'tours.walk.progress': '{visited} sur {total} visitées', + 'tours.walk.visit.visited': 'Visitée', + 'tours.walk.visit.skip': 'Passer', + 'tours.walk.visit.undo': 'Annuler', + 'tours.walk.visit.isVisited': 'Visitée ce jour-là', + 'tours.walk.visit.isSkipped': 'Passée ce jour-là', + 'tours.walk.visit.fromDay': 'Ouvre la balade depuis un jour du plan pour cocher ses étapes ce jour-là.', + 'tours.walk.visit.failed': "L'étape n'a pas pu être cochée. Il faut une connexion ; réessaie dans un instant.", 'tours.planner.tourType': 'Type de sortie', 'tours.paddle.boat': 'Embarcation', 'tours.paddle.boat.kayak': 'Kayak', // same-as-en diff --git a/shared/src/i18n/gr/tours.ts b/shared/src/i18n/gr/tours.ts index 732d69bf0..98acbf794 100644 --- a/shared/src/i18n/gr/tours.ts +++ b/shared/src/i18n/gr/tours.ts @@ -509,6 +509,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'Ο χρόνος με τα πόδια του δρομολογητή ανάμεσα στις στάσεις, χωρίς τις παραμονές.', 'tours.walk.stays': 'Παραμονές', 'tours.walk.totalAtLeast': 'τουλάχιστον {time}', + 'tours.walk.stations': 'Στάσεις της βόλτας', + 'tours.walk.stationNumbered': 'Στάση {number}', + 'tours.walk.progress': '{visited} από {total} επισκέψεις', + 'tours.walk.visit.visited': 'Επισκέφθηκα', + 'tours.walk.visit.skip': 'Παράλειψη', + 'tours.walk.visit.undo': 'Αναίρεση', + 'tours.walk.visit.isVisited': 'Επισκέφθηκε αυτή τη μέρα', + 'tours.walk.visit.isSkipped': 'Παραλείφθηκε αυτή τη μέρα', + 'tours.walk.visit.fromDay': 'Άνοιξε τη βόλτα από μια μέρα του πλάνου για να τσεκάρεις τις στάσεις εκείνης της μέρας.', + 'tours.walk.visit.failed': 'Η στάση δεν σημειώθηκε. Χρειάζεται σύνδεση· δοκίμασε ξανά σε λίγο.', 'tours.planner.tourType': 'Τύπος διαδρομής', 'tours.paddle.boat': 'Σκάφος', 'tours.paddle.boat.kayak': 'Καγιάκ', diff --git a/shared/src/i18n/hu/tours.ts b/shared/src/i18n/hu/tours.ts index 6e0a5cd54..b44f36000 100644 --- a/shared/src/i18n/hu/tours.ts +++ b/shared/src/i18n/hu/tours.ts @@ -501,6 +501,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'Az útvonaltervező gyalogos ideje a megállók között, a tartózkodások nélkül.', 'tours.walk.stays': 'Tartózkodások', 'tours.walk.totalAtLeast': 'legalább {time}', + 'tours.walk.stations': 'A séta megállói', + 'tours.walk.stationNumbered': '{number}. megálló', + 'tours.walk.progress': '{total} megállóból {visited} meglátogatva', + 'tours.walk.visit.visited': 'Meglátogatva', + 'tours.walk.visit.skip': 'Kihagyás', + 'tours.walk.visit.undo': 'Visszavonás', + 'tours.walk.visit.isVisited': 'Ezen a napon meglátogatva', + 'tours.walk.visit.isSkipped': 'Ezen a napon kihagyva', + 'tours.walk.visit.fromDay': 'Nyisd meg a sétát a terv egyik napjáról, hogy arra a napra kipipáld a megállókat.', + 'tours.walk.visit.failed': 'A megállót nem sikerült megjelölni. Ehhez kapcsolat kell; próbáld újra kicsit később.', 'tours.planner.tourType': 'Túratípus', 'tours.paddle.boat': 'Hajó', 'tours.paddle.boat.kayak': 'Kajak', diff --git a/shared/src/i18n/id/tours.ts b/shared/src/i18n/id/tours.ts index dc517ab51..bf00cffce 100644 --- a/shared/src/i18n/id/tours.ts +++ b/shared/src/i18n/id/tours.ts @@ -493,6 +493,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'Waktu jalan kaki dari perute di antara titik singgah, tanpa lama singgah.', 'tours.walk.stays': 'Lama singgah', 'tours.walk.totalAtLeast': 'setidaknya {time}', + 'tours.walk.stations': 'Titik singgah rute', + 'tours.walk.stationNumbered': 'Titik {number}', + 'tours.walk.progress': '{visited} dari {total} dikunjungi', + 'tours.walk.visit.visited': 'Sudah dikunjungi', + 'tours.walk.visit.skip': 'Lewati', + 'tours.walk.visit.undo': 'Batalkan', + 'tours.walk.visit.isVisited': 'Dikunjungi pada hari ini', + 'tours.walk.visit.isSkipped': 'Dilewati pada hari ini', + 'tours.walk.visit.fromDay': 'Buka rute dari salah satu hari di rencana untuk mencentang titiknya pada hari itu.', + 'tours.walk.visit.failed': 'Titik tidak dapat ditandai. Perlu koneksi; coba lagi sebentar lagi.', 'tours.planner.tourType': 'Jenis tur', 'tours.paddle.boat': 'Perahu', 'tours.paddle.boat.kayak': 'Kayak', // same-as-en diff --git a/shared/src/i18n/it/tours.ts b/shared/src/i18n/it/tours.ts index 5c6c37689..1e3aea9cf 100644 --- a/shared/src/i18n/it/tours.ts +++ b/shared/src/i18n/it/tours.ts @@ -500,6 +500,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'Il tempo a piedi del calcolo percorso tra le tappe, senza le soste.', 'tours.walk.stays': 'Soste', 'tours.walk.totalAtLeast': 'almeno {time}', + 'tours.walk.stations': 'Tappe del percorso', + 'tours.walk.stationNumbered': 'Tappa {number}', + 'tours.walk.progress': '{visited} su {total} visitate', + 'tours.walk.visit.visited': 'Visitata', + 'tours.walk.visit.skip': 'Salta', + 'tours.walk.visit.undo': 'Annulla', + 'tours.walk.visit.isVisited': 'Visitata in questo giorno', + 'tours.walk.visit.isSkipped': 'Saltata in questo giorno', + 'tours.walk.visit.fromDay': 'Apri il percorso da un giorno del piano per spuntare le tappe di quel giorno.', + 'tours.walk.visit.failed': 'Non è stato possibile segnare la tappa. Serve una connessione; riprova tra poco.', 'tours.planner.tourType': 'Tipo di tour', 'tours.paddle.boat': 'Imbarcazione', 'tours.paddle.boat.kayak': 'Kayak', // same-as-en diff --git a/shared/src/i18n/ja/tours.ts b/shared/src/i18n/ja/tours.ts index 4c3f03832..d551fba26 100644 --- a/shared/src/i18n/ja/tours.ts +++ b/shared/src/i18n/ja/tours.ts @@ -485,6 +485,17 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'ルート計算による立ち寄り先間の徒歩時間。滞在時間は含みません。', 'tours.walk.stays': '滞在', 'tours.walk.totalAtLeast': '{time} 以上', + 'tours.walk.stations': '立ち寄り先', + 'tours.walk.stationNumbered': '立ち寄り先 {number}', + 'tours.walk.progress': '{total} 件中 {visited} 件訪問済み', + 'tours.walk.visit.visited': '訪問済み', + 'tours.walk.visit.skip': 'スキップ', + 'tours.walk.visit.undo': '取り消す', + 'tours.walk.visit.isVisited': 'この日に訪問済み', + 'tours.walk.visit.isSkipped': 'この日はスキップ', + 'tours.walk.visit.fromDay': '計画の日から開くと、その日の立ち寄り先をチェックできます。', + 'tours.walk.visit.failed': + '立ち寄り先をチェックできませんでした。接続が必要です。少ししてからもう一度お試しください。', 'tours.planner.tourType': 'ツアーの種類', 'tours.paddle.boat': 'ボート', 'tours.paddle.boat.kayak': 'カヤック', diff --git a/shared/src/i18n/ko/tours.ts b/shared/src/i18n/ko/tours.ts index add32e409..0b8b12da6 100644 --- a/shared/src/i18n/ko/tours.ts +++ b/shared/src/i18n/ko/tours.ts @@ -481,6 +481,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': '경로 계산기가 낸 정류지 사이 도보 시간이며, 머무는 시간은 빠져 있습니다.', 'tours.walk.stays': '머무는 시간', 'tours.walk.totalAtLeast': '최소 {time}', + 'tours.walk.stations': '산책 정류지', + 'tours.walk.stationNumbered': '정류지 {number}', + 'tours.walk.progress': '{total}곳 중 {visited}곳 방문', + 'tours.walk.visit.visited': '방문함', + 'tours.walk.visit.skip': '건너뛰기', + 'tours.walk.visit.undo': '되돌리기', + 'tours.walk.visit.isVisited': '이날 방문함', + 'tours.walk.visit.isSkipped': '이날 건너뜀', + 'tours.walk.visit.fromDay': '계획의 날짜에서 열면 그날의 정류지를 체크할 수 있습니다.', + 'tours.walk.visit.failed': '정류지를 표시하지 못했습니다. 연결이 필요하니 잠시 후 다시 시도하세요.', 'tours.planner.tourType': '투어 유형', 'tours.paddle.boat': '보트', 'tours.paddle.boat.kayak': '카약', diff --git a/shared/src/i18n/nl/tours.ts b/shared/src/i18n/nl/tours.ts index 71ba43948..be19fa7fa 100644 --- a/shared/src/i18n/nl/tours.ts +++ b/shared/src/i18n/nl/tours.ts @@ -503,6 +503,17 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'De looptijd van de routeplanner tussen de haltes, zonder het verblijf.', 'tours.walk.stays': 'Verblijven', 'tours.walk.totalAtLeast': 'minstens {time}', + 'tours.walk.stations': 'Haltes van de wandeling', + 'tours.walk.stationNumbered': 'Halte {number}', + 'tours.walk.progress': '{visited} van {total} bezocht', + 'tours.walk.visit.visited': 'Bezocht', + 'tours.walk.visit.skip': 'Overslaan', + 'tours.walk.visit.undo': 'Terugnemen', + 'tours.walk.visit.isVisited': 'Op deze dag bezocht', + 'tours.walk.visit.isSkipped': 'Op deze dag overgeslagen', + 'tours.walk.visit.fromDay': 'Open de wandeling vanuit een dag in het plan om de haltes voor die dag af te vinken.', + 'tours.walk.visit.failed': + 'De halte kon niet worden gemarkeerd. Daar is verbinding voor nodig; probeer het zo opnieuw.', 'tours.planner.tourType': 'Soort tocht', 'tours.paddle.boat': 'Boot', 'tours.paddle.boat.kayak': 'Kajak', diff --git a/shared/src/i18n/pl/tours.ts b/shared/src/i18n/pl/tours.ts index cf25832f9..6911afa09 100644 --- a/shared/src/i18n/pl/tours.ts +++ b/shared/src/i18n/pl/tours.ts @@ -508,6 +508,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'Czas marszu między przystankami według planera trasy, bez postojów.', 'tours.walk.stays': 'Postoje', 'tours.walk.totalAtLeast': 'co najmniej {time}', + 'tours.walk.stations': 'Przystanki trasy', + 'tours.walk.stationNumbered': 'Przystanek {number}', + 'tours.walk.progress': 'Odwiedzono {visited} z {total}', + 'tours.walk.visit.visited': 'Odwiedzony', + 'tours.walk.visit.skip': 'Pomiń', + 'tours.walk.visit.undo': 'Cofnij', + 'tours.walk.visit.isVisited': 'Odwiedzony tego dnia', + 'tours.walk.visit.isSkipped': 'Pominięty tego dnia', + 'tours.walk.visit.fromDay': 'Otwórz trasę z dnia w planie, aby odhaczyć przystanki na ten dzień.', + 'tours.walk.visit.failed': 'Nie udało się oznaczyć przystanku. Potrzebne jest połączenie; spróbuj za chwilę.', 'tours.planner.tourType': 'Rodzaj wycieczki', 'tours.paddle.boat': 'Łódź', 'tours.paddle.boat.kayak': 'Kajak', diff --git a/shared/src/i18n/ru/tours.ts b/shared/src/i18n/ru/tours.ts index d5440e0aa..30dad0018 100644 --- a/shared/src/i18n/ru/tours.ts +++ b/shared/src/i18n/ru/tours.ts @@ -511,6 +511,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'Время пешком между остановками по расчёту маршрута, без остановок на месте.', 'tours.walk.stays': 'Остановки', 'tours.walk.totalAtLeast': 'не меньше {time}', + 'tours.walk.stations': 'Остановки прогулки', + 'tours.walk.stationNumbered': 'Остановка {number}', + 'tours.walk.progress': 'Посещено {visited} из {total}', + 'tours.walk.visit.visited': 'Посещено', + 'tours.walk.visit.skip': 'Пропустить', + 'tours.walk.visit.undo': 'Отменить', + 'tours.walk.visit.isVisited': 'Посещена в этот день', + 'tours.walk.visit.isSkipped': 'Пропущена в этот день', + 'tours.walk.visit.fromDay': 'Открой прогулку из дня в плане, чтобы отмечать остановки за этот день.', + 'tours.walk.visit.failed': 'Не удалось отметить остановку. Нужна связь; попробуй чуть позже.', 'tours.planner.tourType': 'Тип тура', 'tours.paddle.boat': 'Лодка', 'tours.paddle.boat.kayak': 'Каяк', diff --git a/shared/src/i18n/sk/tours.ts b/shared/src/i18n/sk/tours.ts index 5fee76cd4..89d1223ec 100644 --- a/shared/src/i18n/sk/tours.ts +++ b/shared/src/i18n/sk/tours.ts @@ -493,6 +493,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'Čas chôdze medzi zastávkami podľa plánovača trasy, bez pobytov.', 'tours.walk.stays': 'Pobyty', 'tours.walk.totalAtLeast': 'najmenej {time}', + 'tours.walk.stations': 'Zastávky prechádzky', + 'tours.walk.stationNumbered': 'Zastávka {number}', + 'tours.walk.progress': 'Navštívené {visited} z {total}', + 'tours.walk.visit.visited': 'Navštívené', + 'tours.walk.visit.skip': 'Preskočiť', + 'tours.walk.visit.undo': 'Vrátiť', + 'tours.walk.visit.isVisited': 'Navštívené v tento deň', + 'tours.walk.visit.isSkipped': 'Preskočené v tento deň', + 'tours.walk.visit.fromDay': 'Otvor prechádzku z dňa v pláne, aby si odškrtol zastávky na ten deň.', + 'tours.walk.visit.failed': 'Zastávku sa nepodarilo označiť. Je potrebné pripojenie; skús to o chvíľu znova.', 'tours.planner.tourType': 'Typ túry', 'tours.paddle.boat': 'Loď', 'tours.paddle.boat.kayak': 'Kajak', diff --git a/shared/src/i18n/sv/tours.ts b/shared/src/i18n/sv/tours.ts index 7febfc75c..635ea6009 100644 --- a/shared/src/i18n/sv/tours.ts +++ b/shared/src/i18n/sv/tours.ts @@ -492,6 +492,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'Ruttplanerarens gångtid mellan stoppen, utan vistelserna.', 'tours.walk.stays': 'Vistelser', 'tours.walk.totalAtLeast': 'minst {time}', + 'tours.walk.stations': 'Promenadens stopp', + 'tours.walk.stationNumbered': 'Stopp {number}', + 'tours.walk.progress': '{visited} av {total} besökta', + 'tours.walk.visit.visited': 'Besökt', + 'tours.walk.visit.skip': 'Hoppa över', + 'tours.walk.visit.undo': 'Ångra', + 'tours.walk.visit.isVisited': 'Besökt den här dagen', + 'tours.walk.visit.isSkipped': 'Överhoppad den här dagen', + 'tours.walk.visit.fromDay': 'Öppna promenaden från en dag i planen för att bocka av stoppen den dagen.', + 'tours.walk.visit.failed': 'Stoppet kunde inte markeras. Det kräver en anslutning; försök igen om en stund.', 'tours.planner.tourType': 'Turtyp', 'tours.paddle.boat': 'Båt', 'tours.paddle.boat.kayak': 'Kajak', diff --git a/shared/src/i18n/th/tours.ts b/shared/src/i18n/th/tours.ts index 475b7c9b8..1f935c3ac 100644 --- a/shared/src/i18n/th/tours.ts +++ b/shared/src/i18n/th/tours.ts @@ -475,6 +475,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'เวลาเดินระหว่างจุดแวะจากตัวคำนวณเส้นทาง ไม่รวมเวลาแวะพัก', 'tours.walk.stays': 'เวลาแวะพัก', 'tours.walk.totalAtLeast': 'อย่างน้อย {time}', + 'tours.walk.stations': 'จุดแวะของทริป', + 'tours.walk.stationNumbered': 'จุดแวะที่ {number}', + 'tours.walk.progress': 'แวะแล้ว {visited} จาก {total}', + 'tours.walk.visit.visited': 'แวะแล้ว', + 'tours.walk.visit.skip': 'ข้าม', + 'tours.walk.visit.undo': 'ยกเลิก', + 'tours.walk.visit.isVisited': 'แวะแล้วในวันนี้', + 'tours.walk.visit.isSkipped': 'ข้ามไปในวันนี้', + 'tours.walk.visit.fromDay': 'เปิดทริปจากวันในแผนเพื่อติ๊กจุดแวะของวันนั้น', + 'tours.walk.visit.failed': 'ทำเครื่องหมายจุดแวะไม่ได้ ต้องมีการเชื่อมต่อ ลองใหม่อีกครั้งในอีกสักครู่', 'tours.planner.tourType': 'ประเภททริป', 'tours.paddle.boat': 'เรือ', 'tours.paddle.boat.kayak': 'เรือคายัก', diff --git a/shared/src/i18n/tours-locales.spec.ts b/shared/src/i18n/tours-locales.spec.ts index 95c5fd8c5..04d1249b3 100644 --- a/shared/src/i18n/tours-locales.spec.ts +++ b/shared/src/i18n/tours-locales.spec.ts @@ -20,7 +20,7 @@ const tokens = (value: string) => [...value.matchAll(/\{[^{}]+\}/g)].map((match) // placeholder spec; here only the general keys are. const groups = pluralGroups(Object.keys(english)); const generalKeys = (keys: string[]) => keys.filter((key) => !pluralFormOf(key, groups)); -const GENERAL_KEY_COUNT = 735; +const GENERAL_KEY_COUNT = 745; function literalKeys(source: string): string[] { const tree = ts.createSourceFile('locale.ts', source, ts.ScriptTarget.Latest, true); diff --git a/shared/src/i18n/tr/tours.ts b/shared/src/i18n/tr/tours.ts index 0cc37b1b8..74c453236 100644 --- a/shared/src/i18n/tr/tours.ts +++ b/shared/src/i18n/tr/tours.ts @@ -495,6 +495,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'Rota hesaplayıcının duraklar arasındaki yürüme süresi, kalışlar hariç.', 'tours.walk.stays': 'Kalışlar', 'tours.walk.totalAtLeast': 'en az {time}', + 'tours.walk.stations': 'Yürüyüşün durakları', + 'tours.walk.stationNumbered': 'Durak {number}', + 'tours.walk.progress': '{total} duraktan {visited} ziyaret edildi', + 'tours.walk.visit.visited': 'Ziyaret edildi', + 'tours.walk.visit.skip': 'Atla', + 'tours.walk.visit.undo': 'Geri al', + 'tours.walk.visit.isVisited': 'Bu gün ziyaret edildi', + 'tours.walk.visit.isSkipped': 'Bu gün atlandı', + 'tours.walk.visit.fromDay': 'Duraklarını o gün için işaretlemek için yürüyüşü plandaki bir günden aç.', + 'tours.walk.visit.failed': 'Durak işaretlenemedi. Bunun için bağlantı gerekir; birazdan yeniden dene.', 'tours.planner.tourType': 'Tur türü', 'tours.paddle.boat': 'Tekne', 'tours.paddle.boat.kayak': 'Kano (kayak)', diff --git a/shared/src/i18n/uk/tours.ts b/shared/src/i18n/uk/tours.ts index 7f1e1b12f..3d6aa86a3 100644 --- a/shared/src/i18n/uk/tours.ts +++ b/shared/src/i18n/uk/tours.ts @@ -507,6 +507,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'Час пішки між зупинками за розрахунком маршруту, без перебування.', 'tours.walk.stays': 'Перебування', 'tours.walk.totalAtLeast': 'щонайменше {time}', + 'tours.walk.stations': 'Зупинки прогулянки', + 'tours.walk.stationNumbered': 'Зупинка {number}', + 'tours.walk.progress': 'Відвідано {visited} з {total}', + 'tours.walk.visit.visited': 'Відвідано', + 'tours.walk.visit.skip': 'Пропустити', + 'tours.walk.visit.undo': 'Скасувати', + 'tours.walk.visit.isVisited': 'Відвідано цього дня', + 'tours.walk.visit.isSkipped': 'Пропущено цього дня', + 'tours.walk.visit.fromDay': 'Відкрий прогулянку з дня в плані, щоб відмічати зупинки на цей день.', + 'tours.walk.visit.failed': "Не вдалося позначити зупинку. Потрібне з'єднання; спробуй трохи згодом.", 'tours.planner.tourType': 'Тип туру', 'tours.paddle.boat': 'Човен', 'tours.paddle.boat.kayak': 'Каяк', diff --git a/shared/src/i18n/vi/tours.ts b/shared/src/i18n/vi/tours.ts index 87aa5533a..2d8c1aa9d 100644 --- a/shared/src/i18n/vi/tours.ts +++ b/shared/src/i18n/vi/tours.ts @@ -492,6 +492,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': 'Thời gian đi bộ giữa các điểm dừng do bộ định tuyến tính, không gồm thời gian dừng.', 'tours.walk.stays': 'Thời gian dừng', 'tours.walk.totalAtLeast': 'ít nhất {time}', + 'tours.walk.stations': 'Các điểm dừng', + 'tours.walk.stationNumbered': 'Điểm dừng {number}', + 'tours.walk.progress': 'Đã ghé {visited}/{total}', + 'tours.walk.visit.visited': 'Đã ghé', + 'tours.walk.visit.skip': 'Bỏ qua', + 'tours.walk.visit.undo': 'Hoàn tác', + 'tours.walk.visit.isVisited': 'Đã ghé trong ngày này', + 'tours.walk.visit.isSkipped': 'Đã bỏ qua trong ngày này', + 'tours.walk.visit.fromDay': 'Mở lộ trình từ một ngày trong kế hoạch để đánh dấu các điểm dừng của ngày đó.', + 'tours.walk.visit.failed': 'Không thể đánh dấu điểm dừng. Cần có kết nối; hãy thử lại sau giây lát.', 'tours.planner.tourType': 'Loại chuyến', 'tours.paddle.boat': 'Thuyền', 'tours.paddle.boat.kayak': 'Thuyền kayak', diff --git a/shared/src/i18n/zh-TW/tours.ts b/shared/src/i18n/zh-TW/tours.ts index 30460a14b..2af3184e3 100644 --- a/shared/src/i18n/zh-TW/tours.ts +++ b/shared/src/i18n/zh-TW/tours.ts @@ -460,6 +460,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': '路線規劃給出的站點間步行時間,不含停留時間。', 'tours.walk.stays': '停留', 'tours.walk.totalAtLeast': '至少 {time}', + 'tours.walk.stations': '漫步站點', + 'tours.walk.stationNumbered': '第 {number} 站', + 'tours.walk.progress': '已到訪 {visited}/{total}', + 'tours.walk.visit.visited': '已到訪', + 'tours.walk.visit.skip': '略過', + 'tours.walk.visit.undo': '復原', + 'tours.walk.visit.isVisited': '當天已到訪', + 'tours.walk.visit.isSkipped': '當天已略過', + 'tours.walk.visit.fromDay': '從計畫中的某一天打開漫步,即可勾選當天的站點。', + 'tours.walk.visit.failed': '無法標記該站點。需要網路連線,請稍後再試。', 'tours.planner.tourType': '路線類型', 'tours.paddle.boat': '船隻', 'tours.paddle.boat.kayak': '獨木舟(Kayak)', diff --git a/shared/src/i18n/zh/tours.ts b/shared/src/i18n/zh/tours.ts index 6f0939643..2c1f2705c 100644 --- a/shared/src/i18n/zh/tours.ts +++ b/shared/src/i18n/zh/tours.ts @@ -461,6 +461,16 @@ const tours: TranslationStrings = { 'tours.walk.timeEstimatedInfo': '路线规划给出的站点间步行时间,不含停留时间。', 'tours.walk.stays': '停留', 'tours.walk.totalAtLeast': '至少 {time}', + 'tours.walk.stations': '漫步站点', + 'tours.walk.stationNumbered': '第 {number} 站', + 'tours.walk.progress': '已到访 {visited}/{total}', + 'tours.walk.visit.visited': '已到访', + 'tours.walk.visit.skip': '跳过', + 'tours.walk.visit.undo': '撤销', + 'tours.walk.visit.isVisited': '当天已到访', + 'tours.walk.visit.isSkipped': '当天已跳过', + 'tours.walk.visit.fromDay': '从计划中的某一天打开漫步,即可勾选当天的站点。', + 'tours.walk.visit.failed': '无法标记该站点。需要网络连接,请稍后再试。', 'tours.planner.tourType': '路线类型', 'tours.paddle.boat': '船只', 'tours.paddle.boat.kayak': '皮划艇',