From e4b6644be850f6024d8e2852c3a1ff569a7d76fa Mon Sep 17 00:00:00 2001 From: Maurice Date: Mon, 19 Oct 2026 23:15:18 +0200 Subject: [PATCH] feat(tours): a city walk's arrivals against its stops' opening hours, and a compact place search (CW-04, CW-05) Opened from a day with a time, each stop shows its estimated arrival: the start, the walking time shared out along the route, and the stays before it, at the earliest while a stay is unknown. A stop whose opening hours say it is closed then, or closes before the stay ends, says so with the times; hours that cannot be read are shown as they are, and a stop without hours is unknown, never open. The themes are a two by two grid and the categories one line with a list to tick, so the search no longer fills the rail. The button that discards a draft for a new tour says so in two words. --- .../src/components/Tours/TourDetailDialog.tsx | 17 +- .../Tours/poi/TourPoiSearchCard.tsx | 156 ++++++++++--- .../components/Tours/walk/WalkStations.tsx | 219 +++++++++++++----- .../src/components/Tours/walk/walkSchedule.ts | 146 ++++++++++++ shared/src/i18n/ar/tours.ts | 12 +- shared/src/i18n/az/tours.ts | 12 +- shared/src/i18n/br/tours.ts | 12 +- shared/src/i18n/ca/tours.ts | 12 +- shared/src/i18n/cs/tours.ts | 12 +- shared/src/i18n/de/tours.ts | 12 +- shared/src/i18n/en/tours.ts | 12 +- shared/src/i18n/es/tours.ts | 12 +- shared/src/i18n/et/tours.ts | 12 +- shared/src/i18n/fr/tours.ts | 12 +- shared/src/i18n/gr/tours.ts | 12 +- shared/src/i18n/hu/tours.ts | 12 +- shared/src/i18n/id/tours.ts | 12 +- shared/src/i18n/it/tours.ts | 12 +- shared/src/i18n/ja/tours.ts | 12 +- shared/src/i18n/ko/tours.ts | 12 +- shared/src/i18n/nl/tours.ts | 12 +- shared/src/i18n/pl/tours.ts | 12 +- shared/src/i18n/ru/tours.ts | 12 +- shared/src/i18n/sk/tours.ts | 12 +- shared/src/i18n/sv/tours.ts | 12 +- shared/src/i18n/th/tours.ts | 12 +- shared/src/i18n/tours-locales.spec.ts | 2 +- shared/src/i18n/tr/tours.ts | 12 +- shared/src/i18n/uk/tours.ts | 12 +- shared/src/i18n/vi/tours.ts | 12 +- shared/src/i18n/zh-TW/tours.ts | 12 +- shared/src/i18n/zh/tours.ts | 12 +- 32 files changed, 747 insertions(+), 117 deletions(-) create mode 100644 client/src/components/Tours/walk/walkSchedule.ts diff --git a/client/src/components/Tours/TourDetailDialog.tsx b/client/src/components/Tours/TourDetailDialog.tsx index a4f8261e5..5f52ed2b6 100644 --- a/client/src/components/Tours/TourDetailDialog.tsx +++ b/client/src/components/Tours/TourDetailDialog.tsx @@ -654,7 +654,22 @@ export default function TourDetailDialog({ /> )} {tour.tour_type === 'walk' && ( - + day.id === assignment.day_id)?.date ?? null, + start: assignment.assignment_time ?? place.place_time ?? null, + route: analysis?.routeCoordinates ?? [], + walkingMinutes: tour.duration ?? null, + } + : null + } + /> )} {tour.tour_type === 'kayak' && } {tour.tour_type === 'kayak' && } diff --git a/client/src/components/Tours/poi/TourPoiSearchCard.tsx b/client/src/components/Tours/poi/TourPoiSearchCard.tsx index d9707c41e..92807f0a8 100644 --- a/client/src/components/Tours/poi/TourPoiSearchCard.tsx +++ b/client/src/components/Tours/poi/TourPoiSearchCard.tsx @@ -1,16 +1,22 @@ -import type { TourPoi, TourPoiCategory, WalkTheme } from '@trek/shared'; +import type { TourPoi, WalkTheme } from '@trek/shared'; import { TOUR_POI_CORRIDORS_M, WALK_THEMES } from '@trek/shared'; import { AlertTriangle, + ChevronDown, Clock, + Coffee, ExternalLink, + Landmark, Loader2, MoveHorizontal, Plus, RefreshCw, Route, Search, + Shuffle, + Trees, WifiOff, + type LucideIcon, } from 'lucide-react'; import { useEffect, useRef } from 'react'; import { useTranslation } from '../../../i18n'; @@ -18,9 +24,11 @@ import { useSettingsStore } from '../../../store/settingsStore'; import { useTourPoiStore } from '../../../store/tourPoiStore'; import { formatDistance } from '../../../utils/units'; import { TOUR_POI_LOOKS } from '../../Map/poiCategories'; +import { PopoverItem } from '../../Packing/PackingPopover'; import { WhiteBadge } from '../../Planner/planParts'; -import { PillSelect, Segmented } from '../../shared/dialogParts'; +import { PillSelect, PopoverListPanel, Segmented } from '../../shared/dialogParts'; import { fs } from '../../shared/DialogShell'; +import { usePopoverList } from '../../shared/usePopoverList'; import type { TourPlannerController } from '../planner/useTourPlanner'; import { useWaypointPlaceNames } from '../planner/useWaypointPlaceNames'; import { RailCard, TourNotice } from '../tourParts'; @@ -33,21 +41,114 @@ type Unit = Parameters[1]; const km = (metres: number) => metres / 1000; /** A category to search for, as a chip: its icon in its map colour, ticked or not. */ -function CategoryChip({ category, on, onToggle }: { category: TourPoiCategory; on: boolean; onToggle: () => void }) { +/** + * What is searched for, in one line: the chosen categories' icons and names, + * with every category the tour type offers in a list that stays open while + * they are ticked. A theme of a city walk sets them all at once above it. + */ +function CategoryPicker({ search }: { search: TourPoiSearch }) { const { t } = useTranslation(); - const look = TOUR_POI_LOOKS[category]; + const list = usePopoverList(0, { estimatedHeight: 320 }); + const { open, toggle, triggerRef, listId } = list; + const chosen = search.offered.filter((category) => search.categories.includes(category)); + const names = chosen.map((category) => t(TOUR_POI_LOOKS[category].labelKey)); + const summary = + names.length === 0 + ? t('tours.poi.categoriesNone') + : names.length <= 2 + ? names.join(', ') + : t('tours.poi.categoriesMore', { first: names.slice(0, 2).join(', '), more: names.length - 2 }); return ( - + <> + + + {search.offered.map((category) => { + const look = TOUR_POI_LOOKS[category]; + return ( + } + label={t(look.labelKey)} + active={search.categories.includes(category)} + onClick={() => search.toggleCategory(category)} + /> + ); + })} + + + ); +} + +const THEME_ICONS: Record = { culture: Landmark, parks: Trees, food: Coffee, mixed: Shuffle }; + +/** + * CW-05: a city walk's four themes as a grid of two by two, each naming + * itself in full; the one the categories below match is on. + */ +function ThemeGrid({ + themes, + active, + onChoose, +}: { + themes: readonly WalkTheme[]; + active: WalkTheme | null; + onChoose: (theme: WalkTheme) => void; +}) { + const { t } = useTranslation(); + return ( +
+ {themes.map((theme) => { + const Icon = THEME_ICONS[theme]; + const on = theme === active; + return ( + + ); + })} +
); } @@ -313,28 +414,13 @@ export function TourPoiSearchCard({ planner }: { planner: TourPlannerController

{search.themes.length > 0 && (
- - fill - value={search.theme ?? ''} - onChange={(key) => search.chooseTheme(key as WalkTheme)} - label={t('tours.walk.themes')} - options={search.themes.map((key) => ({ value: key, label: t(`tours.walk.theme.${key}`) }))} - /> + {search.theme ? t(`tours.walk.themeHint.${search.theme}`) : t('tours.walk.themeHint.own')}
)} -
- {search.offered.map((category) => ( - search.toggleCategory(category)} - /> - ))} -
+
{t('tours.poi.within')} @@ -381,7 +467,11 @@ export function TourPoiSearchCard({ planner }: { planner: TourPlannerController planner={planner} insertion={insertion} unit={unit} - theme={search.theme && (WALK_THEMES[search.theme] as readonly string[]).includes(poi.category) ? search.theme : null} + theme={ + search.theme && (WALK_THEMES[search.theme] as readonly string[]).includes(poi.category) + ? search.theme + : null + } /> ))} diff --git a/client/src/components/Tours/walk/WalkStations.tsx b/client/src/components/Tours/walk/WalkStations.tsx index e2233fc66..d25537bb9 100644 --- a/client/src/components/Tours/walk/WalkStations.tsx +++ b/client/src/components/Tours/walk/WalkStations.tsx @@ -1,5 +1,5 @@ import type { TourListItem, TourVisitStatus } from '@trek/shared'; -import { Check, CheckCheck, Redo2, SkipForward, Undo2 } from 'lucide-react'; +import { AlertTriangle, Check, CheckCheck, Clock, Redo2, SkipForward, Undo2 } from 'lucide-react'; import { useEffect, useState } from 'react'; import { useTranslation } from '../../../i18n'; import { tourRepo } from '../../../repo/tourRepo'; @@ -10,7 +10,8 @@ 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'; +import { CardAction, TourNotice } from '../tourParts'; +import { clockOf, minutesOf, stationArrivals, stationFit, type StationFit } from './walkSchedule'; let nextId = 0; const freshId = () => `station-${++nextId}`; @@ -81,6 +82,141 @@ function StatusActions({ ); } +/** CW-04: what the arrival at a station meets, said only where it can be known. */ +function StationHours({ fit, arrival, atLeast }: { fit: StationFit; arrival: number; atLeast: boolean }) { + const { t } = useTranslation(); + const time = clockOf(arrival); + const conflict = + fit.kind === 'closed' + ? t('tours.walk.hours.closed', { arrival: time }) + : fit.kind === 'closes' + ? t('tours.walk.hours.closes', { arrival: time, leave: clockOf(fit.leave), closes: clockOf(fit.closes) }) + : null; + return ( + + + + {conflict && ( + + + )} + {fit.kind === 'unread' && ( + + {t('tours.walk.hours.unread', { text: fit.hours })} + + )} + + ); +} + +/** One station of the list: its number or mark, name, stay and the day's arrival, and what can be marked. */ +function StationRow({ + station, + index, + status, + arrival, + date, + onMark, +}: { + station: TourPlannerWaypoint; + index: number; + status: TourVisitStatus | undefined; + arrival: { arrival: number; atLeast: boolean } | null; + date: string | null; + onMark: ((status: TourVisitStatus | 'planned') => void) | null; +}) { + const { t } = useTranslation(); + return ( +
  • + + {status === 'visited' ? ( + + ) : status === 'skipped' ? ( + + ) : ( + index + 1 + )} + + + + {station.name ?? t('tours.walk.stationNumbered', { number: index + 1 })} + + + {arrival && date && ( + + )} + {status && ( + + {t(status === 'visited' ? 'tours.walk.visit.isVisited' : 'tours.walk.visit.isSkipped')} + + )} + + {onMark && } +
  • + ); +} + +/** The lines above the stations: loading, what is missing for arrivals, where the hours come from, where to check off. */ +function WalkStationsNotes({ + loading, + any, + noStart, + hoursShown, + fromDay, +}: { + loading: boolean; + any: boolean; + noStart: boolean; + hoursShown: boolean; + fromDay: boolean; +}) { + const { t } = useTranslation(); + const quiet = (text: string) => ( + + {text} + + ); + if (loading) return quiet(t('common.loading')); + return ( + <> + {any && noStart && quiet(t('tours.walk.hours.noStart'))} + {hoursShown && }>{t('tours.walk.hours.source')}} + {any && fromDay && quiet(t('tours.walk.visit.fromDay'))} + + ); +} + +/** CW-04: what the stations are checked against: the day, its start and the route. */ +export interface WalkDay { + /** `YYYY-MM-DD`; null on a day without a date. */ + date: string | null; + /** `HH:MM` the walk starts that day; null when none is set. */ + start: string | null; + route: ReadonlyArray; + walkingMinutes: number | null; +} + /** * 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 @@ -91,12 +227,15 @@ export function WalkStationsCard({ tripId, assignmentId, canMark, + day = null, }: { 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; + /** CW-04: the day it was opened from, for arrivals and opening hours. */ + day?: WalkDay | null; }) { const { t } = useTranslation(); const toast = useToast(); @@ -108,9 +247,11 @@ export function WalkStationsCard({ 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; + const start = minutesOf(day?.start); + const arrivals = stations && day?.date ? stationArrivals(stations, day.route, start, day.walkingMinutes) : null; + const anyHours = stations?.some((station) => station.openingHours) === true; + const visited = (stations ?? []).filter((station) => station.key && visits?.[station.key] === 'visited').length; + const canCheck = canMark && assignmentId !== null; return (
    - {stations === null && ( - - {t('common.loading')} - - )} - {assignmentId === null && stations && stations.length > 0 && ( - - {t('tours.walk.visit.fromDay')} - - )} + 0} + noStart={!!day && start === null} + hoursShown={!!arrivals && anyHours} + fromDay={assignmentId === null} + />
      - {(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. - ); - })} + {(stations ?? []).map((station, index) => ( + onMark(station.key ?? '', next) : null} + /> + ))}
    diff --git a/client/src/components/Tours/walk/walkSchedule.ts b/client/src/components/Tours/walk/walkSchedule.ts new file mode 100644 index 000000000..a9ea92c27 --- /dev/null +++ b/client/src/components/Tours/walk/walkSchedule.ts @@ -0,0 +1,146 @@ +import type { TourPlannerWaypoint } from '../planner/tourPlannerWaypoints'; + +/** + * CW-04: a city walk's stations against their opening hours on one day. The + * arrival is an estimate: the start of that day's walk, the walking time + * shared out along the route by distance, and the stays before. Hours are + * read only in OpenStreetMap's plain forms ("Mo-Fr 09:00-18:00; Sa 10:00-14:00; + * Su off", "24/7"); anything else stays unread rather than guessed, and a + * station without hours is unknown, never open. + */ + +const DAYS = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'] as const; + +/** Minutes after midnight of a `HH:MM` time; null for anything else. */ +export function minutesOf(time: string | null | undefined): number | null { + const match = /^(\d{1,2}):(\d{2})/.exec(time ?? ''); + if (!match) return null; + const hours = Number(match[1]); + const minutes = Number(match[2]); + return hours < 24 && minutes < 60 ? hours * 60 + minutes : null; +} + +export function clockOf(minutes: number): string { + const day = ((Math.round(minutes) % 1440) + 1440) % 1440; + return `${String(Math.floor(day / 60)).padStart(2, '0')}:${String(day % 60).padStart(2, '0')}`; +} + +/** Monday 0 to Sunday 6, of a `YYYY-MM-DD` date read as a calendar day. */ +function weekdayOf(date: string): number | null { + const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date); + if (!match) return null; + const day = new Date(Date.UTC(Number(match[1]), Number(match[2]) - 1, Number(match[3]))).getUTCDay(); + return (day + 6) % 7; +} + +function daysOf(spec: string): Set | null { + const days = new Set(); + for (const part of spec.split(',')) { + const [from, to] = part.split('-'); + const start = DAYS.indexOf(from as (typeof DAYS)[number]); + const end = to === undefined ? start : DAYS.indexOf(to as (typeof DAYS)[number]); + if (start < 0 || end < 0) return null; + for (let day = start; ; day = (day + 1) % 7) { + days.add(day); + if (day === end) break; + } + } + return days; +} + +/** The windows a place is open on a date, in minutes; null where the hours cannot be read. */ +export function openWindows(hours: string, date: string): Array<[number, number]> | null { + const weekday = weekdayOf(date); + if (weekday === null) return null; + const text = hours.trim(); + if (text === '24/7') return [[0, 1440]]; + let windows: Array<[number, number]> = []; + for (const raw of text.split(';')) { + const rule = raw.trim(); + if (!rule) continue; + const match = /^([A-Za-z,-]+)\s+(.+)$/.exec(rule); + if (!match) return null; + const days = daysOf(match[1]); + if (!days) return null; + if (!days.has(weekday)) continue; + // A later rule for the day replaces an earlier one, as OpenStreetMap reads them. + if (match[2].trim() === 'off' || match[2].trim() === 'closed') { + windows = []; + continue; + } + const ranges: Array<[number, number]> = []; + for (const span of match[2].split(',')) { + const [open, close] = span.trim().split('-'); + const from = minutesOf(open); + const to = close === '24:00' ? 1440 : minutesOf(close); + if (from === null || to === null || to <= from) return null; + ranges.push([from, to]); + } + windows = ranges; + } + return windows; +} + +export type StationFit = + | { kind: 'unknown' } + | { kind: 'unread'; hours: string } + | { kind: 'ok' } + | { kind: 'closed'; arrival: number } + | { kind: 'closes'; arrival: number; leave: number; closes: number }; + +/** Whether a station is open from its arrival to the end of its stay on that date. */ +export function stationFit(hours: string | null | undefined, date: string, arrival: number, stay: number): StationFit { + if (!hours) return { kind: 'unknown' }; + const windows = openWindows(hours, date); + if (windows === null) return { kind: 'unread', hours }; + const leave = arrival + stay; + const open = windows.find(([from, to]) => arrival >= from && arrival < to); + if (!open) return { kind: 'closed', arrival }; + if (leave > open[1]) return { kind: 'closes', arrival, leave, closes: open[1] }; + return { kind: 'ok' }; +} + +/** Metres between two points, close enough along a city walk. */ +function metres(a: readonly [number, number], b: readonly [number, number]): number { + const rad = Math.PI / 180; + const x = (b[1] - a[1]) * rad * Math.cos(((a[0] + b[0]) / 2) * rad); + const y = (b[0] - a[0]) * rad; + return Math.hypot(x, y) * 6_371_000; +} + +/** + * The estimated arrival at each station, in minutes after midnight: the start, + * the walking time shared out by how far along the route the station lies, + * and the stays before it; a stay not known counts as nothing before it and + * makes every later arrival a lower bound. Null when the start, the walking + * time or the route is not known. + */ +export function stationArrivals( + stations: readonly TourPlannerWaypoint[], + route: ReadonlyArray, + start: number | null, + walkingMinutes: number | null +): Array<{ arrival: number; atLeast: boolean }> | null { + if (start === null || walkingMinutes === null || route.length < 2) return null; + const along: number[] = [0]; + for (let i = 1; i < route.length; i += 1) along.push(along[i - 1] + metres(route[i - 1], route[i])); + const total = along[along.length - 1] || 1; + let stays = 0; + let unknown = false; + return stations.map((station) => { + let nearest = 0; + let best = Infinity; + route.forEach((point, index) => { + const gap = metres(point, [station.lat, station.lng]); + if (gap < best) { + best = gap; + nearest = index; + } + }); + const arrival = start + walkingMinutes * (along[nearest] / total) + stays; + const result = { arrival, atLeast: unknown }; + if (station.stayMinutes == null) unknown = true; + else stays += station.stayMinutes; + return result; + }); +} diff --git a/shared/src/i18n/ar/tours.ts b/shared/src/i18n/ar/tours.ts index 12465b44a..05f5f36de 100644 --- a/shared/src/i18n/ar/tours.ts +++ b/shared/src/i18n/ar/tours.ts @@ -119,7 +119,7 @@ const tours: TranslationStrings = { 'tours.planner.discardChanges': 'تجاهل التغييرات', 'tours.planner.newTourConfirmTitle': 'هل تريد بدء جولة جديدة؟', 'tours.planner.newTourConfirmBody': 'ستُحذف تغييراتك غير المحفوظة على هذه الجولة. لن تتغيّر الجولات المحفوظة.', - 'tours.planner.newTourConfirm': 'تجاهل التغييرات وبدء جولة جديدة', + 'tours.planner.newTourConfirm': 'تجاهل وابدأ', 'tours.planner.collapseElevation': 'طيّ مخطط الارتفاع', 'tours.planner.expandElevation': 'توسيع مخطط الارتفاع', 'tours.planner.name': 'اسم الجولة', @@ -520,6 +520,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'قليل من كل شيء: معالم ومتاحف وحدائق وإطلالات ومقاهٍ وأسواق.', 'tours.walk.themeHint.own': 'اختيارك الخاص من الفئات أدناه؛ يضبطها الموضوع لك.', 'tours.walk.themeReason': 'مقترح لموضوع {theme}', + 'tours.walk.arrival': 'الوصول نحو {time}', + 'tours.walk.arrivalAtLeast': 'الوصول في {time} على أقرب تقدير', + 'tours.walk.hours.closed': 'مغلق في {arrival} حسب ساعات العمل', + 'tours.walk.hours.closes': 'الوصول {arrival}، البقاء حتى {leave}، لكنه يغلق في {closes}', + 'tours.walk.hours.unread': 'لم تُقرأ ساعات العمل هنا: {text}', + 'tours.walk.hours.source': + 'ساعات العمل كما في OpenStreetMap مقابل وقت الوصول التقديري؛ تحقق قبل الذهاب. المحطة بلا ساعات غير معروفة، لا مفتوحة.', + 'tours.walk.hours.noStart': 'حدد وقتًا للجولة في هذا اليوم لرؤية أوقات الوصول وساعات العمل.', + 'tours.poi.categoriesNone': 'لم تُختر أي فئة', + 'tours.poi.categoriesMore': '{first} +{more}', '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 a3bf4be65..8d418a277 100644 --- a/shared/src/i18n/az/tours.ts +++ b/shared/src/i18n/az/tours.ts @@ -106,7 +106,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'Yeni tur başlansın?', 'tours.planner.newTourConfirmBody': 'Bu turda saxlanılmamış dəyişiklikləriniz ləğv ediləcək. Saxlanılmış turlar dəyişməyəcək.', - 'tours.planner.newTourConfirm': 'Dəyişiklikləri ləğv et və yeni tur başlat', + 'tours.planner.newTourConfirm': 'Ləğv et və başla', 'tours.planner.collapseElevation': 'Yüksəklik profilini yığ', 'tours.planner.expandElevation': 'Yüksəklik profilini aç', 'tours.planner.name': 'Turun adı', @@ -516,6 +516,16 @@ const tours: TranslationStrings = { 'Hər şeydən bir az: görməli yerlər, muzeylər, parklar, mənzərələr, kafelər və bazarlar.', 'tours.walk.themeHint.own': 'Aşağıda öz kateqoriya seçimin; mövzu onları sənin üçün qurur.', 'tours.walk.themeReason': '{theme} üçün təklif olunur', + 'tours.walk.arrival': 'Təxmini çatma {time}', + 'tours.walk.arrivalAtLeast': 'Ən tez {time} çatma', + 'tours.walk.hours.closed': 'Açılış saatlarına görə {arrival} vaxtı bağlıdır', + 'tours.walk.hours.closes': 'Çatma {arrival}, qalma {leave} qədər, amma {closes} vaxtı bağlanır', + 'tours.walk.hours.unread': 'Açılış saatları burada oxunmadı: {text}', + 'tours.walk.hours.source': + 'Açılış saatları OpenStreetMap-dakı kimi, təxmini çatma vaxtı ilə; getməzdən əvvəl yoxla. Saatı olmayan dayanacaq məlum deyil, açıq deyil.', + 'tours.walk.hours.noStart': 'Çatma vaxtlarını və açılış saatlarını görmək üçün gəzintiyə bu gün vaxt ver.', + 'tours.poi.categoriesNone': 'Kateqoriya seçilməyib', + 'tours.poi.categoriesMore': '{first} +{more}', '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 2146724eb..c242a9823 100644 --- a/shared/src/i18n/br/tours.ts +++ b/shared/src/i18n/br/tours.ts @@ -110,7 +110,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'Começar um novo passeio?', 'tours.planner.newTourConfirmBody': 'As alterações não salvas deste passeio serão descartadas. Os passeios salvos não serão alterados.', - 'tours.planner.newTourConfirm': 'Descartar e começar novo passeio', + 'tours.planner.newTourConfirm': 'Descartar e começar', 'tours.planner.collapseElevation': 'Recolher perfil de elevação', 'tours.planner.expandElevation': 'Expandir perfil de elevação', 'tours.planner.name': 'Nome do passeio', @@ -521,6 +521,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'Um pouco de cada: pontos turísticos, museus, parques, vistas, cafeterias e feiras.', 'tours.walk.themeHint.own': 'Sua própria seleção de categorias abaixo; um tema as define para você.', 'tours.walk.themeReason': 'Sugerido para {theme}', + 'tours.walk.arrival': 'Chegada por volta de {time}', + 'tours.walk.arrivalAtLeast': 'Chegada a partir de {time}', + 'tours.walk.hours.closed': 'Fechado às {arrival} segundo o horário', + 'tours.walk.hours.closes': 'Chegada {arrival}, permanência até {leave}, mas fecha às {closes}', + 'tours.walk.hours.unread': 'Horário não lido aqui: {text}', + 'tours.walk.hours.source': + 'Horário como está no OpenStreetMap, comparado à chegada estimada; confira antes de ir. Uma parada sem horário é desconhecida, não aberta.', + 'tours.walk.hours.noStart': 'Dê ao passeio um horário neste dia para ver chegadas e horários.', + 'tours.poi.categoriesNone': 'Nenhuma categoria escolhida', + 'tours.poi.categoriesMore': '{first} +{more}', '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 ec3a59565..1d4f94bb6 100644 --- a/shared/src/i18n/ca/tours.ts +++ b/shared/src/i18n/ca/tours.ts @@ -110,7 +110,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'Vols començar una nova excursió?', 'tours.planner.newTourConfirmBody': "Es descartaran els canvis sense desar d'aquesta excursió. Les excursions desades no es modificaran.", - 'tours.planner.newTourConfirm': 'Descarta i comença una nova excursió', + 'tours.planner.newTourConfirm': 'Descarta i comença', 'tours.planner.collapseElevation': "Replega el perfil d'altitud", 'tours.planner.expandElevation': "Desplega el perfil d'altitud", 'tours.planner.name': "Nom de l'excursió", @@ -522,6 +522,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': "Una mica de cada: llocs d'interès, museus, parcs, vistes, cafeteries i mercats.", 'tours.walk.themeHint.own': 'La teva pròpia tria de categories a sota; un tema te les defineix.', 'tours.walk.themeReason': 'Suggerit per a {theme}', + 'tours.walk.arrival': 'Arribada cap a les {time}', + 'tours.walk.arrivalAtLeast': 'Arribada com a aviat a les {time}', + 'tours.walk.hours.closed': "Tancat a les {arrival} segons l'horari", + 'tours.walk.hours.closes': 'Arribada {arrival}, estada fins a les {leave}, però tanca a les {closes}', + 'tours.walk.hours.unread': 'Horari no llegit aquí: {text}', + 'tours.walk.hours.source': + "Horari tal com el té OpenStreetMap, contra l'arribada estimada; comprova-ho abans d'anar-hi. Una parada sense horari és desconeguda, no oberta.", + 'tours.walk.hours.noStart': 'Posa una hora a la ruta aquest dia per veure arribades i horaris.', + 'tours.poi.categoriesNone': 'Cap categoria triada', + 'tours.poi.categoriesMore': '{first} +{more}', '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 202120300..11fdc06ae 100644 --- a/shared/src/i18n/cs/tours.ts +++ b/shared/src/i18n/cs/tours.ts @@ -109,7 +109,7 @@ const tours: TranslationStrings = { 'tours.planner.discardChanges': 'Zahodit změny', 'tours.planner.newTourConfirmTitle': 'Začít nový výlet?', 'tours.planner.newTourConfirmBody': 'Neuložené změny tohoto výletu budou zahozeny. Uložené výlety se nezmění.', - 'tours.planner.newTourConfirm': 'Zahodit změny a začít nový výlet', + 'tours.planner.newTourConfirm': 'Zahodit a začít', 'tours.planner.collapseElevation': 'Sbalit výškový profil', 'tours.planner.expandElevation': 'Rozbalit výškový profil', 'tours.planner.name': 'Název výletu', @@ -512,6 +512,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'Od všeho trochu: památky, muzea, parky, výhledy, kavárny a trhy.', 'tours.walk.themeHint.own': 'Tvůj vlastní výběr kategorií níže; téma je nastaví za tebe.', 'tours.walk.themeReason': 'Navrženo pro {theme}', + 'tours.walk.arrival': 'Příchod kolem {time}', + 'tours.walk.arrivalAtLeast': 'Příchod nejdříve {time}', + 'tours.walk.hours.closed': 'V {arrival} podle otevírací doby zavřeno', + 'tours.walk.hours.closes': 'Příchod {arrival}, pobyt do {leave}, ale zavírá v {closes}', + 'tours.walk.hours.unread': 'Otevírací dobu zde nelze vyhodnotit: {text}', + 'tours.walk.hours.source': + 'Otevírací doba podle OpenStreetMap proti odhadovanému příchodu; před cestou si ji ověř. Zastávka bez časů je neznámá, ne otevřená.', + 'tours.walk.hours.noStart': 'Dej procházce v tento den čas, abys viděl příchody a otevírací doby.', + 'tours.poi.categoriesNone': 'Žádná kategorie', + 'tours.poi.categoriesMore': '{first} +{more}', '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 d3e788ff5..644298a7b 100644 --- a/shared/src/i18n/de/tours.ts +++ b/shared/src/i18n/de/tours.ts @@ -111,7 +111,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'Neue Tour beginnen?', 'tours.planner.newTourConfirmBody': 'Ungespeicherte Änderungen an dieser Tour gehen verloren. Gespeicherte Touren bleiben erhalten.', - 'tours.planner.newTourConfirm': 'Änderungen verwerfen und neue Tour beginnen', + 'tours.planner.newTourConfirm': 'Verwerfen und neu', 'tours.planner.collapseElevation': 'Höhenprofil einklappen', 'tours.planner.expandElevation': 'Höhenprofil ausklappen', 'tours.planner.name': 'Name der Tour', @@ -527,6 +527,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'Von allem etwas: Sehenswürdigkeiten, Museen, Parks, Aussicht, Cafés und Märkte.', 'tours.walk.themeHint.own': 'Deine eigene Auswahl darunter; ein Thema stellt sie für dich zusammen.', 'tours.walk.themeReason': 'Vorgeschlagen für {theme}', + 'tours.walk.arrival': 'Ankunft ca. {time}', + 'tours.walk.arrivalAtLeast': 'Ankunft frühestens {time}', + 'tours.walk.hours.closed': 'Um {arrival} laut Öffnungszeiten geschlossen', + 'tours.walk.hours.closes': 'Ankunft {arrival}, Aufenthalt bis {leave}, aber Schließung um {closes}', + 'tours.walk.hours.unread': 'Öffnungszeiten hier nicht auswertbar: {text}', + 'tours.walk.hours.source': + 'Öffnungszeiten wie bei OpenStreetMap eingetragen, gegen die geschätzte Ankunft; vor Ort lieber prüfen. Eine Station ohne Zeiten ist unbekannt, nicht geöffnet.', + 'tours.walk.hours.noStart': 'Gib dem Walk an diesem Tag eine Uhrzeit, um Ankünfte und Öffnungszeiten zu sehen.', + 'tours.poi.categoriesNone': 'Keine Kategorie gewählt', + 'tours.poi.categoriesMore': '{first} +{more}', '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 ccb4af1ac..4b1a6b73f 100644 --- a/shared/src/i18n/en/tours.ts +++ b/shared/src/i18n/en/tours.ts @@ -106,7 +106,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'Start a new tour?', 'tours.planner.newTourConfirmBody': 'Your unsaved changes to this tour will be discarded. Saved tours will not be changed.', - 'tours.planner.newTourConfirm': 'Discard changes and start new tour', + 'tours.planner.newTourConfirm': 'Discard and start', 'tours.planner.collapseElevation': 'Collapse elevation profile', 'tours.planner.expandElevation': 'Expand elevation profile', 'tours.planner.name': 'Tour name', @@ -513,6 +513,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'Some of each: sights, museums, parks, views, coffee shops and markets.', 'tours.walk.themeHint.own': 'Your own pick of categories below; a theme sets them for you.', 'tours.walk.themeReason': 'Suggested for {theme}', + 'tours.walk.arrival': 'Arrival about {time}', + 'tours.walk.arrivalAtLeast': 'Arrival {time} at the earliest', + 'tours.walk.hours.closed': 'Closed at {arrival} by its opening hours', + 'tours.walk.hours.closes': 'Arrival {arrival}, stay until {leave}, but it closes at {closes}', + 'tours.walk.hours.unread': 'Opening hours not read here: {text}', + 'tours.walk.hours.source': + 'Opening hours as OpenStreetMap has them, against the estimated arrival; check before you go. A stop without hours is unknown, not open.', + 'tours.walk.hours.noStart': 'Give the walk a time on this day to see arrivals and opening hours.', + 'tours.poi.categoriesNone': 'No category chosen', + 'tours.poi.categoriesMore': '{first} +{more}', '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 103c7df54..e871b43d0 100644 --- a/shared/src/i18n/es/tours.ts +++ b/shared/src/i18n/es/tours.ts @@ -111,7 +111,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': '¿Empezar una nueva excursión?', 'tours.planner.newTourConfirmBody': 'Se descartarán los cambios sin guardar de esta excursión. Las excursiones guardadas no se modificarán.', - 'tours.planner.newTourConfirm': 'Descartar y empezar nueva excursión', + 'tours.planner.newTourConfirm': 'Descartar y empezar', 'tours.planner.collapseElevation': 'Contraer perfil de altitud', 'tours.planner.expandElevation': 'Expandir perfil de altitud', 'tours.planner.name': 'Nombre de la excursión', @@ -520,6 +520,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'Un poco de cada: lugares de interés, museos, parques, vistas, cafeterías y mercados.', 'tours.walk.themeHint.own': 'Tu propia selección de categorías abajo; un tema las elige por ti.', 'tours.walk.themeReason': 'Sugerido para {theme}', + 'tours.walk.arrival': 'Llegada hacia las {time}', + 'tours.walk.arrivalAtLeast': 'Llegada no antes de las {time}', + 'tours.walk.hours.closed': 'Cerrado a las {arrival} según su horario', + 'tours.walk.hours.closes': 'Llegada {arrival}, estancia hasta las {leave}, pero cierra a las {closes}', + 'tours.walk.hours.unread': 'Horario no interpretado aquí: {text}', + 'tours.walk.hours.source': + 'Horario tal como figura en OpenStreetMap, frente a la llegada estimada; compruébalo antes de ir. Una parada sin horario es desconocida, no abierta.', + 'tours.walk.hours.noStart': 'Pon al paseo una hora en este día para ver llegadas y horarios.', + 'tours.poi.categoriesNone': 'Ninguna categoría elegida', + 'tours.poi.categoriesMore': '{first} +{more}', '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 97a12e18d..42edb375a 100644 --- a/shared/src/i18n/et/tours.ts +++ b/shared/src/i18n/et/tours.ts @@ -107,7 +107,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'Kas alustada uut retke?', 'tours.planner.newTourConfirmBody': 'Selle retke salvestamata muudatused lähevad kaotsi. Salvestatud retki ei muudeta.', - 'tours.planner.newTourConfirm': 'Loobu muudatustest ja alusta uut retke', + 'tours.planner.newTourConfirm': 'Loobu ja alusta', 'tours.planner.collapseElevation': 'Ahenda kõrgusprofiil', 'tours.planner.expandElevation': 'Laienda kõrgusprofiil', 'tours.planner.name': 'Retke nimi', @@ -515,6 +515,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'Igaühest natuke: vaatamisväärsused, muuseumid, pargid, vaated, kohvikud ja turud.', 'tours.walk.themeHint.own': 'Sinu enda kategooriate valik allpool; teema seab need sinu eest.', 'tours.walk.themeReason': 'Soovitatud teemale {theme}', + 'tours.walk.arrival': 'Saabumine umbes {time}', + 'tours.walk.arrivalAtLeast': 'Saabumine kõige varem {time}', + 'tours.walk.hours.closed': 'Lahtiolekuaegade järgi kell {arrival} suletud', + 'tours.walk.hours.closes': 'Saabumine {arrival}, peatus kuni {leave}, aga suletakse kell {closes}', + 'tours.walk.hours.unread': 'Lahtiolekuaegu siin ei loetud: {text}', + 'tours.walk.hours.source': + 'Lahtiolekuajad nagu OpenStreetMapis, võrreldes hinnangulise saabumisega; kontrolli enne minekut. Aegadeta peatus on teadmata, mitte avatud.', + 'tours.walk.hours.noStart': 'Anna jalutusele sel päeval kellaaeg, et näha saabumisi ja lahtiolekuaegu.', + 'tours.poi.categoriesNone': 'Kategooriat pole valitud', + 'tours.poi.categoriesMore': '{first} +{more}', '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 cca0bb7b6..3e1133ca2 100644 --- a/shared/src/i18n/fr/tours.ts +++ b/shared/src/i18n/fr/tours.ts @@ -113,7 +113,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'Commencer une nouvelle sortie ?', 'tours.planner.newTourConfirmBody': 'Les modifications non enregistrées de cette sortie seront perdues. Les sorties enregistrées resteront inchangées.', - 'tours.planner.newTourConfirm': 'Abandonner et commencer une nouvelle sortie', + 'tours.planner.newTourConfirm': 'Abandonner et commencer', 'tours.planner.collapseElevation': 'Réduire le profil altimétrique', 'tours.planner.expandElevation': 'Déployer le profil altimétrique', 'tours.planner.name': 'Nom de la sortie', @@ -530,6 +530,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'Un peu de chaque : sites, musées, parcs, points de vue, cafés et marchés.', 'tours.walk.themeHint.own': 'Ta propre sélection de catégories ci-dessous ; un thème les choisit pour toi.', 'tours.walk.themeReason': 'Suggéré pour {theme}', + 'tours.walk.arrival': 'Arrivée vers {time}', + 'tours.walk.arrivalAtLeast': 'Arrivée au plus tôt à {time}', + 'tours.walk.hours.closed': 'Fermé à {arrival} selon ses horaires', + 'tours.walk.hours.closes': "Arrivée {arrival}, arrêt jusqu'à {leave}, mais fermeture à {closes}", + 'tours.walk.hours.unread': 'Horaires non lus ici : {text}', + 'tours.walk.hours.source': + "Horaires tels qu'indiqués sur OpenStreetMap, face à l'arrivée estimée ; vérifie avant d'y aller. Une étape sans horaires est inconnue, pas ouverte.", + 'tours.walk.hours.noStart': 'Donne une heure à la balade ce jour-là pour voir les arrivées et les horaires.', + 'tours.poi.categoriesNone': 'Aucune catégorie choisie', + 'tours.poi.categoriesMore': '{first} +{more}', '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 ba06abcf2..ddcd3b758 100644 --- a/shared/src/i18n/gr/tours.ts +++ b/shared/src/i18n/gr/tours.ts @@ -111,7 +111,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'Έναρξη νέας εκδρομής;', 'tours.planner.newTourConfirmBody': 'Οι μη αποθηκευμένες αλλαγές αυτής της εκδρομής θα απορριφθούν. Οι αποθηκευμένες εκδρομές δεν θα αλλάξουν.', - 'tours.planner.newTourConfirm': 'Απόρριψη αλλαγών και έναρξη νέας εκδρομής', + 'tours.planner.newTourConfirm': 'Απόρριψη και έναρξη', 'tours.planner.collapseElevation': 'Σύμπτυξη υψομετρικού προφίλ', 'tours.planner.expandElevation': 'Ανάπτυξη υψομετρικού προφίλ', 'tours.planner.name': 'Όνομα εκδρομής', @@ -530,6 +530,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': "Λίγο απ' όλα: αξιοθέατα, μουσεία, πάρκα, θέα, καφετέριες και αγορές.", 'tours.walk.themeHint.own': 'Η δική σου επιλογή κατηγοριών παρακάτω· ένα θέμα τις ορίζει για σένα.', 'tours.walk.themeReason': 'Προτείνεται για {theme}', + 'tours.walk.arrival': 'Άφιξη περίπου {time}', + 'tours.walk.arrivalAtLeast': 'Άφιξη το νωρίτερο {time}', + 'tours.walk.hours.closed': 'Κλειστό στις {arrival} σύμφωνα με το ωράριο', + 'tours.walk.hours.closes': 'Άφιξη {arrival}, παραμονή έως {leave}, αλλά κλείνει στις {closes}', + 'tours.walk.hours.unread': 'Το ωράριο δεν διαβάστηκε εδώ: {text}', + 'tours.walk.hours.source': + 'Ωράριο όπως στο OpenStreetMap, σε σχέση με την εκτιμώμενη άφιξη· έλεγξε πριν πας. Στάση χωρίς ωράριο είναι άγνωστη, όχι ανοιχτή.', + 'tours.walk.hours.noStart': 'Δώσε ώρα στη βόλτα αυτή τη μέρα για να δεις αφίξεις και ωράρια.', + 'tours.poi.categoriesNone': 'Δεν επιλέχθηκε κατηγορία', + 'tours.poi.categoriesMore': '{first} +{more}', '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 b6c32925b..02a5c0b9c 100644 --- a/shared/src/i18n/hu/tours.ts +++ b/shared/src/i18n/hu/tours.ts @@ -107,7 +107,7 @@ const tours: TranslationStrings = { 'tours.planner.discardChanges': 'Módosítások elvetése', 'tours.planner.newTourConfirmTitle': 'Új túrát kezdesz?', 'tours.planner.newTourConfirmBody': 'A túra nem mentett módosításai elvesznek. A mentett túrák nem változnak.', - 'tours.planner.newTourConfirm': 'Módosítások elvetése és új túra kezdése', + 'tours.planner.newTourConfirm': 'Elvetés és új', 'tours.planner.collapseElevation': 'Magassági profil összecsukása', 'tours.planner.expandElevation': 'Magassági profil kibontása', 'tours.planner.name': 'Túra neve', @@ -522,6 +522,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'Mindenből egy kicsit: látnivalók, múzeumok, parkok, kilátás, kávézók és piacok.', 'tours.walk.themeHint.own': 'Saját kategóriaválasztásod lent; egy téma beállítja helyetted.', 'tours.walk.themeReason': 'Javasolva: {theme}', + 'tours.walk.arrival': 'Érkezés kb. {time}', + 'tours.walk.arrivalAtLeast': 'Érkezés legkorábban {time}', + 'tours.walk.hours.closed': '{arrival} órakor a nyitvatartás szerint zárva', + 'tours.walk.hours.closes': 'Érkezés {arrival}, tartózkodás {leave}-ig, de {closes}-kor zár', + 'tours.walk.hours.unread': 'A nyitvatartás itt nem értelmezhető: {text}', + 'tours.walk.hours.source': + 'Nyitvatartás az OpenStreetMap szerint, a becsült érkezéshez mérve; indulás előtt ellenőrizd. Nyitvatartás nélküli megálló ismeretlen, nem nyitott.', + 'tours.walk.hours.noStart': 'Adj időpontot a sétának ezen a napon, hogy lásd az érkezéseket és a nyitvatartást.', + 'tours.poi.categoriesNone': 'Nincs kategória kiválasztva', + 'tours.poi.categoriesMore': '{first} +{more}', '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 9703c47c1..446319e79 100644 --- a/shared/src/i18n/id/tours.ts +++ b/shared/src/i18n/id/tours.ts @@ -105,7 +105,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'Mulai tur baru?', 'tours.planner.newTourConfirmBody': 'Perubahan yang belum disimpan pada tur ini akan dibuang. Tur yang sudah disimpan tidak akan diubah.', - 'tours.planner.newTourConfirm': 'Buang perubahan dan mulai tur baru', + 'tours.planner.newTourConfirm': 'Buang dan mulai', 'tours.planner.collapseElevation': 'Ciutkan profil elevasi', 'tours.planner.expandElevation': 'Perluas profil elevasi', 'tours.planner.name': 'Nama tur', @@ -515,6 +515,16 @@ const tours: TranslationStrings = { 'Sedikit dari semuanya: tempat wisata, museum, taman, pemandangan, kedai kopi, dan pasar.', 'tours.walk.themeHint.own': 'Pilihan kategorimu sendiri di bawah; tema mengaturnya untukmu.', 'tours.walk.themeReason': 'Disarankan untuk {theme}', + 'tours.walk.arrival': 'Tiba sekitar {time}', + 'tours.walk.arrivalAtLeast': 'Tiba paling cepat {time}', + 'tours.walk.hours.closed': 'Tutup pada {arrival} menurut jam bukanya', + 'tours.walk.hours.closes': 'Tiba {arrival}, singgah sampai {leave}, tetapi tutup pukul {closes}', + 'tours.walk.hours.unread': 'Jam buka tidak terbaca di sini: {text}', + 'tours.walk.hours.source': + 'Jam buka sesuai OpenStreetMap, dibandingkan dengan perkiraan tiba; periksa sebelum berangkat. Titik tanpa jam buka tidak diketahui, bukan buka.', + 'tours.walk.hours.noStart': 'Beri rute waktu mulai di hari ini untuk melihat waktu tiba dan jam buka.', + 'tours.poi.categoriesNone': 'Belum ada kategori', + 'tours.poi.categoriesMore': '{first} +{more}', '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 5613ec331..97083c9fc 100644 --- a/shared/src/i18n/it/tours.ts +++ b/shared/src/i18n/it/tours.ts @@ -109,7 +109,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'Iniziare una nuova escursione?', 'tours.planner.newTourConfirmBody': 'Le modifiche non salvate a questa escursione verranno scartate. Le escursioni salvate non verranno modificate.', - 'tours.planner.newTourConfirm': 'Scarta e inizia nuova escursione', + 'tours.planner.newTourConfirm': 'Scarta e inizia', 'tours.planner.collapseElevation': 'Comprimi profilo altimetrico', 'tours.planner.expandElevation': 'Espandi profilo altimetrico', 'tours.planner.name': "Nome dell'escursione", @@ -521,6 +521,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': "Un po' di tutto: attrazioni, musei, parchi, panorami, caffetterie e mercati.", 'tours.walk.themeHint.own': 'La tua scelta di categorie qui sotto; un tema le imposta per te.', 'tours.walk.themeReason': 'Suggerito per {theme}', + 'tours.walk.arrival': 'Arrivo verso le {time}', + 'tours.walk.arrivalAtLeast': 'Arrivo non prima delle {time}', + 'tours.walk.hours.closed': 'Chiuso alle {arrival} secondo gli orari', + 'tours.walk.hours.closes': 'Arrivo {arrival}, sosta fino alle {leave}, ma chiude alle {closes}', + 'tours.walk.hours.unread': 'Orari non interpretati qui: {text}', + 'tours.walk.hours.source': + "Orari come riportati su OpenStreetMap, rispetto all'arrivo stimato; verifica prima di andare. Una tappa senza orari è sconosciuta, non aperta.", + 'tours.walk.hours.noStart': 'Dai un orario al percorso in questo giorno per vedere arrivi e orari.', + 'tours.poi.categoriesNone': 'Nessuna categoria scelta', + 'tours.poi.categoriesMore': '{first} +{more}', '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 621c483fe..45c7ca170 100644 --- a/shared/src/i18n/ja/tours.ts +++ b/shared/src/i18n/ja/tours.ts @@ -103,7 +103,7 @@ const tours: TranslationStrings = { 'tours.planner.discardChanges': '変更を破棄', 'tours.planner.newTourConfirmTitle': '新しいツアーを開始しますか?', 'tours.planner.newTourConfirmBody': 'このツアーの未保存の変更は破棄されます。保存済みのツアーは変更されません。', - 'tours.planner.newTourConfirm': '変更を破棄して新しいツアーを開始', + 'tours.planner.newTourConfirm': '破棄して開始', 'tours.planner.collapseElevation': '標高グラフを折りたたむ', 'tours.planner.expandElevation': '標高グラフを展開', 'tours.planner.name': 'ツアー名', @@ -507,6 +507,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': '観光名所、美術館・博物館、公園、眺望、カフェ、市場を少しずつ。', 'tours.walk.themeHint.own': '下のカテゴリーは自分で選択中。テーマを選ぶと自動で設定されます。', 'tours.walk.themeReason': '{theme} のおすすめ', + 'tours.walk.arrival': '到着 {time} ごろ', + 'tours.walk.arrivalAtLeast': '到着は早くても {time}', + 'tours.walk.hours.closed': '営業時間では {arrival} は閉まっています', + 'tours.walk.hours.closes': '{arrival} 到着、{leave} まで滞在予定ですが {closes} に閉まります', + 'tours.walk.hours.unread': '営業時間はここでは読み取れません:{text}', + 'tours.walk.hours.source': + 'OpenStreetMap の営業時間を推定到着時刻と照合しています。出かける前に確認してください。営業時間のない立ち寄り先は「不明」で、営業中とはみなしません。', + 'tours.walk.hours.noStart': 'この日の開始時刻を設定すると、到着時刻と営業時間が表示されます。', + 'tours.poi.categoriesNone': 'カテゴリー未選択', + 'tours.poi.categoriesMore': '{first} +{more}', '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 9b09af9cb..c8f6735ec 100644 --- a/shared/src/i18n/ko/tours.ts +++ b/shared/src/i18n/ko/tours.ts @@ -101,7 +101,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': '새 투어를 시작할까요?', 'tours.planner.newTourConfirmBody': '이 투어의 저장하지 않은 변경 사항은 삭제됩니다. 저장된 투어는 변경되지 않습니다.', - 'tours.planner.newTourConfirm': '변경 사항을 버리고 새 투어 시작', + 'tours.planner.newTourConfirm': '버리고 시작', 'tours.planner.collapseElevation': '고도 그래프 접기', 'tours.planner.expandElevation': '고도 그래프 펼치기', 'tours.planner.name': '투어 이름', @@ -502,6 +502,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': '명소, 박물관, 공원, 전망, 카페, 시장을 골고루.', 'tours.walk.themeHint.own': '아래 카테고리는 직접 고른 것입니다. 테마를 고르면 자동으로 설정됩니다.', 'tours.walk.themeReason': '{theme} 추천', + 'tours.walk.arrival': '도착 약 {time}', + 'tours.walk.arrivalAtLeast': '도착 빨라야 {time}', + 'tours.walk.hours.closed': '영업시간상 {arrival}에는 문을 닫습니다', + 'tours.walk.hours.closes': '{arrival} 도착, {leave}까지 머물 예정이지만 {closes}에 문을 닫습니다', + 'tours.walk.hours.unread': '여기서는 영업시간을 읽지 못했습니다: {text}', + 'tours.walk.hours.source': + 'OpenStreetMap의 영업시간을 예상 도착 시간과 비교합니다. 가기 전에 확인하세요. 영업시간이 없는 정류지는 영업 중이 아니라 미상입니다.', + 'tours.walk.hours.noStart': '이날 산책에 시간을 지정하면 도착 시간과 영업시간을 볼 수 있습니다.', + 'tours.poi.categoriesNone': '선택한 카테고리 없음', + 'tours.poi.categoriesMore': '{first} +{more}', '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 2fc7cd096..30f8cba04 100644 --- a/shared/src/i18n/nl/tours.ts +++ b/shared/src/i18n/nl/tours.ts @@ -111,7 +111,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'Een nieuwe tocht starten?', 'tours.planner.newTourConfirmBody': 'Je niet-opgeslagen wijzigingen aan deze tocht worden verworpen. Opgeslagen tochten blijven ongewijzigd.', - 'tours.planner.newTourConfirm': 'Verwerpen en nieuwe tocht starten', + 'tours.planner.newTourConfirm': 'Weggooien en beginnen', 'tours.planner.collapseElevation': 'Hoogteprofiel inklappen', 'tours.planner.expandElevation': 'Hoogteprofiel uitklappen', 'tours.planner.name': 'Naam van de tocht', @@ -525,6 +525,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'Van alles wat: bezienswaardigheden, musea, parken, uitzicht, koffiezaken en markten.', 'tours.walk.themeHint.own': 'Je eigen keuze van categorieën hieronder; een thema stelt ze voor je in.', 'tours.walk.themeReason': 'Voorgesteld voor {theme}', + 'tours.walk.arrival': 'Aankomst rond {time}', + 'tours.walk.arrivalAtLeast': 'Aankomst op zijn vroegst {time}', + 'tours.walk.hours.closed': 'Om {arrival} gesloten volgens de openingstijden', + 'tours.walk.hours.closes': 'Aankomst {arrival}, verblijf tot {leave}, maar sluit om {closes}', + 'tours.walk.hours.unread': 'Openingstijden hier niet gelezen: {text}', + 'tours.walk.hours.source': + 'Openingstijden zoals in OpenStreetMap, tegen de geschatte aankomst; controleer ze voor je gaat. Een halte zonder tijden is onbekend, niet open.', + 'tours.walk.hours.noStart': 'Geef de wandeling op deze dag een tijd om aankomsten en openingstijden te zien.', + 'tours.poi.categoriesNone': 'Geen categorie gekozen', + 'tours.poi.categoriesMore': '{first} +{more}', '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 76e2b88e7..0fe21cf39 100644 --- a/shared/src/i18n/pl/tours.ts +++ b/shared/src/i18n/pl/tours.ts @@ -117,7 +117,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'Rozpocząć nową wycieczkę?', 'tours.planner.newTourConfirmBody': 'Niezapisane zmiany tej wycieczki zostaną odrzucone. Zapisane wycieczki nie zostaną zmienione.', - 'tours.planner.newTourConfirm': 'Odrzuć zmiany i rozpocznij nową wycieczkę', + 'tours.planner.newTourConfirm': 'Odrzuć i zacznij', 'tours.planner.collapseElevation': 'Zwiń profil wysokościowy', 'tours.planner.expandElevation': 'Rozwiń profil wysokościowy', 'tours.planner.name': 'Nazwa wycieczki', @@ -529,6 +529,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'Po trochu wszystkiego: atrakcje, muzea, parki, widoki, kawiarnie i targi.', 'tours.walk.themeHint.own': 'Twój własny wybór kategorii poniżej; motyw ustawi je za ciebie.', 'tours.walk.themeReason': 'Proponowane dla: {theme}', + 'tours.walk.arrival': 'Przyjście około {time}', + 'tours.walk.arrivalAtLeast': 'Przyjście najwcześniej {time}', + 'tours.walk.hours.closed': 'Według godzin otwarcia o {arrival} zamknięte', + 'tours.walk.hours.closes': 'Przyjście {arrival}, pobyt do {leave}, ale zamknięcie o {closes}', + 'tours.walk.hours.unread': 'Godziny otwarcia nieodczytane: {text}', + 'tours.walk.hours.source': + 'Godziny otwarcia z OpenStreetMap wobec szacowanego przyjścia; sprawdź przed wyjściem. Przystanek bez godzin jest nieznany, nie otwarty.', + 'tours.walk.hours.noStart': 'Ustaw godzinę trasy tego dnia, aby zobaczyć przyjścia i godziny otwarcia.', + 'tours.poi.categoriesNone': 'Nie wybrano kategorii', + 'tours.poi.categoriesMore': '{first} +{more}', '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 970580999..c4216a7dd 100644 --- a/shared/src/i18n/ru/tours.ts +++ b/shared/src/i18n/ru/tours.ts @@ -118,7 +118,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'Начать новый тур?', 'tours.planner.newTourConfirmBody': 'Несохранённые изменения этого тура будут отменены. Сохранённые туры не изменятся.', - 'tours.planner.newTourConfirm': 'Отменить изменения и начать новый тур', + 'tours.planner.newTourConfirm': 'Сбросить и начать', 'tours.planner.collapseElevation': 'Свернуть профиль высот', 'tours.planner.expandElevation': 'Развернуть профиль высот', 'tours.planner.name': 'Название тура', @@ -532,6 +532,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'Всего понемногу: достопримечательности, музеи, парки, виды, кафе и рынки.', 'tours.walk.themeHint.own': 'Твой собственный выбор категорий ниже; тема задаст их за тебя.', 'tours.walk.themeReason': 'Предложено для темы {theme}', + 'tours.walk.arrival': 'Прибытие около {time}', + 'tours.walk.arrivalAtLeast': 'Прибытие не раньше {time}', + 'tours.walk.hours.closed': 'По часам работы в {arrival} закрыто', + 'tours.walk.hours.closes': 'Прибытие {arrival}, остановка до {leave}, но закрывается в {closes}', + 'tours.walk.hours.unread': 'Часы работы здесь не разобраны: {text}', + 'tours.walk.hours.source': + 'Часы работы как в OpenStreetMap, относительно расчётного прибытия; проверь перед выходом. Остановка без часов неизвестна, а не открыта.', + 'tours.walk.hours.noStart': 'Задай прогулке время в этот день, чтобы видеть прибытия и часы работы.', + 'tours.poi.categoriesNone': 'Категория не выбрана', + 'tours.poi.categoriesMore': '{first} +{more}', '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 736c457d8..35c9d1b4a 100644 --- a/shared/src/i18n/sk/tours.ts +++ b/shared/src/i18n/sk/tours.ts @@ -110,7 +110,7 @@ const tours: TranslationStrings = { 'tours.planner.discardChanges': 'Zahodiť zmeny', 'tours.planner.newTourConfirmTitle': 'Začať novú túru?', 'tours.planner.newTourConfirmBody': 'Neuložené zmeny tejto túry sa zahodia. Uložené túry zostanú nezmenené.', - 'tours.planner.newTourConfirm': 'Zahodiť zmeny a začať novú túru', + 'tours.planner.newTourConfirm': 'Zahodiť a začať', 'tours.planner.collapseElevation': 'Zbaliť výškový profil', 'tours.planner.expandElevation': 'Rozbaliť výškový profil', 'tours.planner.name': 'Názov túry', @@ -514,6 +514,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'Od všetkého trochu: pamiatky, múzeá, parky, výhľady, kaviarne a trhy.', 'tours.walk.themeHint.own': 'Tvoj vlastný výber kategórií nižšie; téma ich nastaví za teba.', 'tours.walk.themeReason': 'Navrhnuté pre {theme}', + 'tours.walk.arrival': 'Príchod okolo {time}', + 'tours.walk.arrivalAtLeast': 'Príchod najskôr {time}', + 'tours.walk.hours.closed': 'Podľa otváracích hodín o {arrival} zatvorené', + 'tours.walk.hours.closes': 'Príchod {arrival}, pobyt do {leave}, ale zatvára o {closes}', + 'tours.walk.hours.unread': 'Otváracie hodiny tu nemožno vyhodnotiť: {text}', + 'tours.walk.hours.source': + 'Otváracie hodiny podľa OpenStreetMap oproti odhadovanému príchodu; pred cestou si ich over. Zastávka bez časov je neznáma, nie otvorená.', + 'tours.walk.hours.noStart': 'Daj prechádzke v tento deň čas, aby si videl príchody a otváracie hodiny.', + 'tours.poi.categoriesNone': 'Žiadna kategória', + 'tours.poi.categoriesMore': '{first} +{more}', '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 09ca72793..834725954 100644 --- a/shared/src/i18n/sv/tours.ts +++ b/shared/src/i18n/sv/tours.ts @@ -106,7 +106,7 @@ const tours: TranslationStrings = { 'tours.planner.discardChanges': 'Kasta ändringar', 'tours.planner.newTourConfirmTitle': 'Börja på en ny tur?', 'tours.planner.newTourConfirmBody': 'Dina osparade ändringar i den här turen kastas. Sparade turer ändras inte.', - 'tours.planner.newTourConfirm': 'Kasta ändringar och börja på ny tur', + 'tours.planner.newTourConfirm': 'Släng och börja', 'tours.planner.collapseElevation': 'Fäll ihop höjdprofil', 'tours.planner.expandElevation': 'Fäll ut höjdprofil', 'tours.planner.name': 'Turens namn', @@ -513,6 +513,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'Lite av allt: sevärdheter, museer, parker, utsikter, kaféer och torghandel.', 'tours.walk.themeHint.own': 'Ditt eget urval av kategorier nedan; ett tema väljer dem åt dig.', 'tours.walk.themeReason': 'Föreslaget för {theme}', + 'tours.walk.arrival': 'Ankomst runt {time}', + 'tours.walk.arrivalAtLeast': 'Ankomst tidigast {time}', + 'tours.walk.hours.closed': 'Stängt kl. {arrival} enligt öppettiderna', + 'tours.walk.hours.closes': 'Ankomst {arrival}, vistelse till {leave}, men stänger kl. {closes}', + 'tours.walk.hours.unread': 'Öppettiderna kunde inte läsas här: {text}', + 'tours.walk.hours.source': + 'Öppettider enligt OpenStreetMap, mot beräknad ankomst; kontrollera innan du går. Ett stopp utan tider är okänt, inte öppet.', + 'tours.walk.hours.noStart': 'Ge promenaden en tid den här dagen för att se ankomster och öppettider.', + 'tours.poi.categoriesNone': 'Ingen kategori vald', + 'tours.poi.categoriesMore': '{first} +{more}', '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 04f1659e0..6db7489f9 100644 --- a/shared/src/i18n/th/tours.ts +++ b/shared/src/i18n/th/tours.ts @@ -100,7 +100,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'เริ่มทัวร์ใหม่ไหม?', 'tours.planner.newTourConfirmBody': 'การเปลี่ยนแปลงของทัวร์นี้ที่ยังไม่บันทึกจะถูกทิ้ง ทัวร์ที่บันทึกแล้วจะไม่เปลี่ยนแปลง', - 'tours.planner.newTourConfirm': 'ทิ้งการเปลี่ยนแปลงและเริ่มทัวร์ใหม่', + 'tours.planner.newTourConfirm': 'ทิ้งแล้วเริ่มใหม่', 'tours.planner.collapseElevation': 'ยุบกราฟระดับความสูง', 'tours.planner.expandElevation': 'ขยายกราฟระดับความสูง', 'tours.planner.name': 'ชื่อทัวร์', @@ -496,6 +496,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'มีทุกอย่าง: สถานที่น่าสนใจ พิพิธภัณฑ์ สวน จุดชมวิว ร้านกาแฟ และตลาด', 'tours.walk.themeHint.own': 'หมวดหมู่ด้านล่างเลือกเอง ธีมจะตั้งค่าให้อัตโนมัติ', 'tours.walk.themeReason': 'แนะนำสำหรับ {theme}', + 'tours.walk.arrival': 'ถึงประมาณ {time}', + 'tours.walk.arrivalAtLeast': 'ถึงเร็วที่สุด {time}', + 'tours.walk.hours.closed': 'ตามเวลาทำการ ปิดเวลา {arrival}', + 'tours.walk.hours.closes': 'ถึง {arrival} แวะถึง {leave} แต่ปิด {closes}', + 'tours.walk.hours.unread': 'อ่านเวลาทำการไม่ได้: {text}', + 'tours.walk.hours.source': + 'เวลาทำการตาม OpenStreetMap เทียบกับเวลาถึงโดยประมาณ โปรดตรวจสอบก่อนไป จุดแวะที่ไม่มีเวลาทำการถือว่าไม่ทราบ ไม่ใช่เปิดอยู่', + 'tours.walk.hours.noStart': 'กำหนดเวลาให้ทริปในวันนี้เพื่อดูเวลาถึงและเวลาทำการ', + 'tours.poi.categoriesNone': 'ยังไม่ได้เลือกหมวดหมู่', + 'tours.poi.categoriesMore': '{first} +{more}', '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 d7494d8f1..fa814cb47 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 = 756; +const GENERAL_KEY_COUNT = 765; 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 f14ab3610..32831b96c 100644 --- a/shared/src/i18n/tr/tours.ts +++ b/shared/src/i18n/tr/tours.ts @@ -106,7 +106,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'Yeni bir tur başlatılsın mı?', 'tours.planner.newTourConfirmBody': 'Bu turdaki kaydedilmemiş değişiklikler silinecek. Kaydedilmiş turlar değişmeyecek.', - 'tours.planner.newTourConfirm': 'Değişiklikleri sil ve yeni tur başlat', + 'tours.planner.newTourConfirm': 'Vazgeç ve başla', 'tours.planner.collapseElevation': 'Yükseklik profilini daralt', 'tours.planner.expandElevation': 'Yükseklik profilini genişlet', 'tours.planner.name': 'Tur adı', @@ -517,6 +517,16 @@ const tours: TranslationStrings = { 'Her şeyden biraz: görülecek yerler, müzeler, parklar, manzaralar, kafeler ve pazarlar.', 'tours.walk.themeHint.own': 'Aşağıdaki kategorileri kendin seçtin; bir tema onları senin için ayarlar.', 'tours.walk.themeReason': '{theme} için önerildi', + 'tours.walk.arrival': 'Varış yaklaşık {time}', + 'tours.walk.arrivalAtLeast': 'En erken varış {time}', + 'tours.walk.hours.closed': 'Açılış saatlerine göre {arrival} saatinde kapalı', + 'tours.walk.hours.closes': 'Varış {arrival}, kalış {leave} saatine kadar, ama {closes} saatinde kapanıyor', + 'tours.walk.hours.unread': 'Açılış saatleri burada okunamadı: {text}', + 'tours.walk.hours.source': + "Açılış saatleri OpenStreetMap'teki gibi, tahmini varışa göre; gitmeden önce kontrol et. Saati olmayan durak bilinmiyordur, açık değildir.", + 'tours.walk.hours.noStart': 'Varışları ve açılış saatlerini görmek için yürüyüşe bu gün bir saat ver.', + 'tours.poi.categoriesNone': 'Kategori seçilmedi', + 'tours.poi.categoriesMore': '{first} +{more}', '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 e2a49feac..5b8d50879 100644 --- a/shared/src/i18n/uk/tours.ts +++ b/shared/src/i18n/uk/tours.ts @@ -115,7 +115,7 @@ const tours: TranslationStrings = { 'tours.planner.discardChanges': 'Скасувати зміни', 'tours.planner.newTourConfirmTitle': 'Почати новий тур?', 'tours.planner.newTourConfirmBody': 'Незбережені зміни цього туру буде скасовано. Збережені тури не зміняться.', - 'tours.planner.newTourConfirm': 'Скасувати зміни й почати новий тур', + 'tours.planner.newTourConfirm': 'Скинути й почати', 'tours.planner.collapseElevation': 'Згорнути профіль висот', 'tours.planner.expandElevation': 'Розгорнути профіль висот', 'tours.planner.name': 'Назва туру', @@ -528,6 +528,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': "Усього потроху: пам'ятки, музеї, парки, краєвиди, кав'ярні та ринки.", 'tours.walk.themeHint.own': 'Твій власний вибір категорій нижче; тема задасть їх за тебе.', 'tours.walk.themeReason': 'Запропоновано для теми {theme}', + 'tours.walk.arrival': 'Прибуття близько {time}', + 'tours.walk.arrivalAtLeast': 'Прибуття не раніше {time}', + 'tours.walk.hours.closed': 'За годинами роботи о {arrival} зачинено', + 'tours.walk.hours.closes': 'Прибуття {arrival}, перебування до {leave}, але зачиняється о {closes}', + 'tours.walk.hours.unread': 'Години роботи тут не розібрано: {text}', + 'tours.walk.hours.source': + 'Години роботи як в OpenStreetMap, відносно розрахункового прибуття; перевір перед виходом. Зупинка без годин невідома, а не відкрита.', + 'tours.walk.hours.noStart': 'Задай прогулянці час цього дня, щоб бачити прибуття й години роботи.', + 'tours.poi.categoriesNone': 'Категорію не вибрано', + 'tours.poi.categoriesMore': '{first} +{more}', '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 8fa3f3504..a63052ff8 100644 --- a/shared/src/i18n/vi/tours.ts +++ b/shared/src/i18n/vi/tours.ts @@ -107,7 +107,7 @@ const tours: TranslationStrings = { 'tours.planner.newTourConfirmTitle': 'Bắt đầu chuyến tham quan mới?', 'tours.planner.newTourConfirmBody': 'Các thay đổi chưa lưu của chuyến tham quan này sẽ bị bỏ. Các chuyến tham quan đã lưu sẽ không thay đổi.', - 'tours.planner.newTourConfirm': 'Bỏ thay đổi và bắt đầu chuyến tham quan mới', + 'tours.planner.newTourConfirm': 'Hủy và bắt đầu', 'tours.planner.collapseElevation': 'Thu gọn biểu đồ độ cao', 'tours.planner.expandElevation': 'Mở rộng biểu đồ độ cao', 'tours.planner.name': 'Tên chuyến tham quan', @@ -513,6 +513,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': 'Mỗi thứ một chút: điểm tham quan, bảo tàng, công viên, cảnh đẹp, quán cà phê và chợ.', 'tours.walk.themeHint.own': 'Bạn đang tự chọn danh mục bên dưới; chủ đề sẽ chọn giúp bạn.', 'tours.walk.themeReason': 'Gợi ý cho {theme}', + 'tours.walk.arrival': 'Đến khoảng {time}', + 'tours.walk.arrivalAtLeast': 'Đến sớm nhất lúc {time}', + 'tours.walk.hours.closed': 'Theo giờ mở cửa, lúc {arrival} đóng cửa', + 'tours.walk.hours.closes': 'Đến {arrival}, ở đến {leave}, nhưng đóng cửa lúc {closes}', + 'tours.walk.hours.unread': 'Không đọc được giờ mở cửa ở đây: {text}', + 'tours.walk.hours.source': + 'Giờ mở cửa theo OpenStreetMap, so với giờ đến ước tính; hãy kiểm tra trước khi đi. Điểm dừng không có giờ là chưa rõ, không phải đang mở.', + 'tours.walk.hours.noStart': 'Đặt giờ cho lộ trình trong ngày này để xem giờ đến và giờ mở cửa.', + 'tours.poi.categoriesNone': 'Chưa chọn danh mục', + 'tours.poi.categoriesMore': '{first} +{more}', '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 3aa1f26e3..707aab9be 100644 --- a/shared/src/i18n/zh-TW/tours.ts +++ b/shared/src/i18n/zh-TW/tours.ts @@ -96,7 +96,7 @@ const tours: TranslationStrings = { 'tours.planner.discardChanges': '捨棄變更', 'tours.planner.newTourConfirmTitle': '開始新的遊程?', 'tours.planner.newTourConfirmBody': '這個遊程中尚未儲存的變更將被捨棄。已儲存的遊程不會變更。', - 'tours.planner.newTourConfirm': '捨棄變更並開始新遊程', + 'tours.planner.newTourConfirm': '放棄並開始', 'tours.planner.collapseElevation': '收合海拔剖面圖', 'tours.planner.expandElevation': '展開海拔剖面圖', 'tours.planner.name': '遊程名稱', @@ -481,6 +481,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': '各類都有一些:景點、博物館、公園、景觀、咖啡館和市集。', 'tours.walk.themeHint.own': '下方類別為你自選;選擇主題會自動設定。', 'tours.walk.themeReason': '推薦用於{theme}', + 'tours.walk.arrival': '約 {time} 抵達', + 'tours.walk.arrivalAtLeast': '最早 {time} 抵達', + 'tours.walk.hours.closed': '按營業時間,{arrival} 時已關門', + 'tours.walk.hours.closes': '{arrival} 抵達,停留至 {leave},但 {closes} 關門', + 'tours.walk.hours.unread': '此處無法解析營業時間:{text}', + 'tours.walk.hours.source': + '營業時間以 OpenStreetMap 為準,並與預計抵達時間對照;出發前請再確認。沒有營業時間的站點視為未知,而非營業中。', + 'tours.walk.hours.noStart': '為當天的漫步設定時間,即可查看抵達時間和營業時間。', + 'tours.poi.categoriesNone': '未選擇類別', + 'tours.poi.categoriesMore': '{first} +{more}', '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 e12db79fd..4319c87a2 100644 --- a/shared/src/i18n/zh/tours.ts +++ b/shared/src/i18n/zh/tours.ts @@ -97,7 +97,7 @@ const tours: TranslationStrings = { 'tours.planner.discardChanges': '放弃更改', 'tours.planner.newTourConfirmTitle': '开始规划新游览路线?', 'tours.planner.newTourConfirmBody': '此游览路线中未保存的更改将被丢弃。已保存的游览路线不会改变。', - 'tours.planner.newTourConfirm': '放弃更改并开始新游览路线', + 'tours.planner.newTourConfirm': '放弃并开始', 'tours.planner.collapseElevation': '收起海拔剖面图', 'tours.planner.expandElevation': '展开海拔剖面图', 'tours.planner.name': '游览名称', @@ -482,6 +482,16 @@ const tours: TranslationStrings = { 'tours.walk.themeHint.mixed': '各类都有一些:景点、博物馆、公园、景观、咖啡馆和集市。', 'tours.walk.themeHint.own': '下方类别为你自选;选择主题会自动设置。', 'tours.walk.themeReason': '推荐用于{theme}', + 'tours.walk.arrival': '约 {time} 到达', + 'tours.walk.arrivalAtLeast': '最早 {time} 到达', + 'tours.walk.hours.closed': '按营业时间,{arrival} 时已关门', + 'tours.walk.hours.closes': '{arrival} 到达,停留至 {leave},但 {closes} 关门', + 'tours.walk.hours.unread': '此处无法解析营业时间:{text}', + 'tours.walk.hours.source': + '营业时间以 OpenStreetMap 为准,并与预计到达时间对照;出发前请再确认。没有营业时间的站点视为未知,而非营业中。', + 'tours.walk.hours.noStart': '为当天的漫步设定时间,即可查看到达时间和营业时间。', + 'tours.poi.categoriesNone': '未选择类别', + 'tours.poi.categoriesMore': '{first} +{more}', 'tours.planner.tourType': '路线类型', 'tours.paddle.boat': '船只', 'tours.paddle.boat.kayak': '皮划艇',