mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-21 06:26:26 +00:00
refactor(roadtrip): put the driving limits behind a button, and the walk beside the stay
Two things the column was getting wrong. The limits sat open as a card, three rows of a narrow sidebar for something set once on the first day of planning and left alone after; they are behind a proper full-width button now, with what is currently set riding on the button so the form does not have to be open to read it. And the walk from the road was its own line under the stay, where it looked like an unrelated note; it wears the stay's two-part badge and sits beside it, because both answer the same question about a stop — one in time, one in a walk.
This commit is contained in:
@@ -1,17 +1,24 @@
|
||||
import React from 'react'
|
||||
import { Clock, Fuel, CalendarClock } from 'lucide-react'
|
||||
import React, { useState } from 'react'
|
||||
import { Clock, Fuel, CalendarClock, SlidersHorizontal, ChevronRight } from 'lucide-react'
|
||||
import Modal from '../shared/Modal'
|
||||
import { useTranslation } from '../../i18n/TranslationContext'
|
||||
import { useSettingsStore } from '../../store/settingsStore'
|
||||
import { convertDistance } from '../../utils/units'
|
||||
import { convertDistance, formatDistance } from '../../utils/units'
|
||||
import { formatDurationShort } from './roadtripModel'
|
||||
import type { DistanceUnit } from '../../types'
|
||||
import { FS } from './typeScale'
|
||||
|
||||
/**
|
||||
* The three numbers that decide when the rail speaks up about the driving.
|
||||
*
|
||||
* Behind a button rather than laid out in the column, because this is set once and then
|
||||
* read never: a permanent card costs three rows of a narrow sidebar for something most
|
||||
* travellers touch on the first day of planning and leave alone after. The button carries
|
||||
* the current answer, so what is set stays visible without the form being.
|
||||
*
|
||||
* All three are personal rather than instance configuration: how long somebody is willing
|
||||
* to sit behind a wheel, and how far their car goes on a tank, belong to the traveller.
|
||||
* They are stored as plain per-user settings, which is why this needs no migration.
|
||||
* They are plain per-user settings, which is why this needs no migration.
|
||||
*
|
||||
* Nothing here suggests a stop, adds one, or knows anything about a vehicle. OSM has no
|
||||
* idea what car this is, and neither has TREK, so the range is a number the traveller
|
||||
@@ -33,9 +40,9 @@ function LimitRow({ icon: Icon, label, suffix, value, placeholder, onChange }: {
|
||||
onChange: (next: number) => void
|
||||
}): React.ReactElement {
|
||||
return (
|
||||
<label className="flex items-center gap-2.5">
|
||||
<Icon size={14} className="shrink-0 text-content-faint" aria-hidden />
|
||||
<span className="min-w-0 flex-1 text-content-secondary" style={{ fontSize: FS.label }}>{label}</span>
|
||||
<label className="flex items-center gap-3">
|
||||
<Icon size={16} className="shrink-0 text-content-faint" aria-hidden />
|
||||
<span className="min-w-0 flex-1 text-body text-content-secondary">{label}</span>
|
||||
<input
|
||||
type="number"
|
||||
inputMode="numeric"
|
||||
@@ -43,72 +50,109 @@ function LimitRow({ icon: Icon, label, suffix, value, placeholder, onChange }: {
|
||||
value={value || ''}
|
||||
placeholder={placeholder}
|
||||
onChange={e => onChange(parseLimit(e.target.value))}
|
||||
className="w-16 rounded-md border border-edge bg-surface px-1.5 py-0.5 text-end tabular-nums text-content focus:border-accent focus:outline-none"
|
||||
style={{ fontSize: FS.label }}
|
||||
className="w-20 rounded-lg border border-edge bg-surface px-2 py-1 text-end text-body tabular-nums text-content focus:border-accent focus:outline-none"
|
||||
/>
|
||||
<span className="w-6 text-content-faint" style={{ fontSize: FS.label }}>{suffix}</span>
|
||||
<span className="w-7 text-caption text-content-faint">{suffix}</span>
|
||||
</label>
|
||||
)
|
||||
}
|
||||
|
||||
export default function RoadtripLimitsCard({ onSave }: {
|
||||
/** Persists one setting. Absent leaves the card read-only. */
|
||||
/** Persists one setting. Absent leaves the dialog read-only. */
|
||||
onSave?: (key: string, value: number) => void
|
||||
}): React.ReactElement {
|
||||
const { t } = useTranslation()
|
||||
const settings = useSettingsStore(s => s.settings)
|
||||
const [open, setOpen] = useState(false)
|
||||
const distanceUnit: DistanceUnit = settings.distance_unit === 'imperial' ? 'imperial' : 'metric'
|
||||
|
||||
const legMinutes = settings.roadtrip_leg_minutes
|
||||
const dayMinutes = settings.roadtrip_day_minutes
|
||||
const rangeKm = settings.roadtrip_range_km
|
||||
|
||||
// Kilometres in storage, the traveller's own unit on screen. Without the round trip an
|
||||
// imperial user types 400 meaning miles, 400 km gets stored, and the warnings arrive a
|
||||
// third too early for ever after.
|
||||
const rangeShown = settings.roadtrip_range_km
|
||||
? Math.round(convertDistance(settings.roadtrip_range_km, distanceUnit))
|
||||
: undefined
|
||||
const rangeShown = rangeKm ? Math.round(convertDistance(rangeKm, distanceUnit)) : undefined
|
||||
const setRange = (shown: number) => {
|
||||
const km = distanceUnit === 'imperial' ? shown / 0.621371 : shown
|
||||
onSave?.('roadtrip_range_km', Math.round(km))
|
||||
}
|
||||
|
||||
// What is set, on the button itself, so the form does not have to be open to see it.
|
||||
// Nothing set reads as "off" rather than as an empty string, which would look broken.
|
||||
const summary = [
|
||||
legMinutes ? formatDurationShort(legMinutes * 60) : null,
|
||||
dayMinutes ? formatDurationShort(dayMinutes * 60) : null,
|
||||
rangeKm ? formatDistance(rangeKm, distanceUnit) : null,
|
||||
].filter(Boolean).join(' · ')
|
||||
|
||||
return (
|
||||
<section className="rounded-xl border border-edge-faint bg-surface-card p-3.5">
|
||||
<h3 className="mb-2.5 font-geist font-semibold uppercase tracking-[0.15em] text-content-faint" style={{ fontSize: FS.label }}>
|
||||
{t('roadtrip.limit.title')}
|
||||
</h3>
|
||||
<div className="flex flex-col gap-2">
|
||||
<LimitRow
|
||||
icon={Clock}
|
||||
label={t('roadtrip.limit.legLabel')}
|
||||
suffix={t('roadtrip.limit.minutes')}
|
||||
value={settings.roadtrip_leg_minutes}
|
||||
placeholder={t('roadtrip.limit.off')}
|
||||
onChange={v => onSave?.('roadtrip_leg_minutes', v)}
|
||||
/>
|
||||
<LimitRow
|
||||
icon={CalendarClock}
|
||||
label={t('roadtrip.limit.dayLabel')}
|
||||
suffix={t('roadtrip.limit.minutes')}
|
||||
value={settings.roadtrip_day_minutes}
|
||||
placeholder={t('roadtrip.limit.off')}
|
||||
onChange={v => onSave?.('roadtrip_day_minutes', v)}
|
||||
/>
|
||||
<LimitRow
|
||||
icon={Fuel}
|
||||
label={t('roadtrip.limit.rangeLabel')}
|
||||
suffix={distanceUnit === 'imperial' ? 'mi' : 'km'}
|
||||
value={rangeShown}
|
||||
placeholder={t('roadtrip.limit.off')}
|
||||
onChange={setRange}
|
||||
/>
|
||||
</div>
|
||||
{/* Said out loud rather than left to be discovered: the day figure this is measured
|
||||
against is driving time only. It does not include how long anyone stands still,
|
||||
and it does not include the leg to the hotel, because the rail does not model
|
||||
one. A limit called "travel time per day" over that number would be a lie that
|
||||
looks tidy. */}
|
||||
<p className="mt-2.5 text-content-faint" style={{ fontSize: FS.label }}>
|
||||
{t('roadtrip.limit.hint')}
|
||||
</p>
|
||||
</section>
|
||||
<>
|
||||
{/* Full width and card-sized, the same weight as the search controls above it: this
|
||||
is one of three things this column does, not a footnote under the other two.
|
||||
The current answer rides on the button, so what is set stays visible without the
|
||||
form being open. */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(true)}
|
||||
className="flex w-full items-center gap-3 rounded-xl border border-edge-faint bg-surface-card px-3.5 py-3 text-start transition-colors hover:bg-surface-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent"
|
||||
>
|
||||
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-surface-tertiary text-content-secondary">
|
||||
<SlidersHorizontal size={16} aria-hidden />
|
||||
</span>
|
||||
<span className="flex min-w-0 flex-1 flex-col gap-0.5">
|
||||
<span className="truncate text-body font-semibold text-content">
|
||||
{t('roadtrip.limit.title')}
|
||||
</span>
|
||||
<span className={`truncate tabular-nums ${summary ? 'text-content-secondary' : 'text-content-faint'}`} style={{ fontSize: FS.label }}>
|
||||
{summary || t('roadtrip.limit.none')}
|
||||
</span>
|
||||
</span>
|
||||
<ChevronRight size={16} className="shrink-0 text-content-faint" aria-hidden />
|
||||
</button>
|
||||
|
||||
{open ? (
|
||||
<Modal isOpen onClose={() => setOpen(false)} size="sm" title={
|
||||
<span className="flex items-center gap-2">
|
||||
<SlidersHorizontal size={15} className="text-content-faint" aria-hidden />
|
||||
{t('roadtrip.limit.title')}
|
||||
</span>
|
||||
}>
|
||||
<div className="flex flex-col gap-4">
|
||||
<LimitRow
|
||||
icon={Clock}
|
||||
label={t('roadtrip.limit.legLabel')}
|
||||
suffix={t('roadtrip.limit.minutes')}
|
||||
value={legMinutes}
|
||||
placeholder={t('roadtrip.limit.off')}
|
||||
onChange={v => onSave?.('roadtrip_leg_minutes', v)}
|
||||
/>
|
||||
<LimitRow
|
||||
icon={CalendarClock}
|
||||
label={t('roadtrip.limit.dayLabel')}
|
||||
suffix={t('roadtrip.limit.minutes')}
|
||||
value={dayMinutes}
|
||||
placeholder={t('roadtrip.limit.off')}
|
||||
onChange={v => onSave?.('roadtrip_day_minutes', v)}
|
||||
/>
|
||||
<LimitRow
|
||||
icon={Fuel}
|
||||
label={t('roadtrip.limit.rangeLabel')}
|
||||
suffix={distanceUnit === 'imperial' ? 'mi' : 'km'}
|
||||
value={rangeShown}
|
||||
placeholder={t('roadtrip.limit.off')}
|
||||
onChange={setRange}
|
||||
/>
|
||||
{/* Said out loud rather than left to be discovered: the day figure this is
|
||||
measured against is driving time only. It does not include how long anyone
|
||||
stands still, and it does not include the leg to the hotel, because the
|
||||
rail does not model one. A limit called "travel time per day" over that
|
||||
number would be a lie that looks tidy. */}
|
||||
<p className="text-caption text-content-faint">{t('roadtrip.limit.hint')}</p>
|
||||
</div>
|
||||
</Modal>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -96,6 +96,37 @@ const DAY_BADGE = 'inline-flex h-[20px] items-center rounded-lg px-2 font-geist
|
||||
* Read-only for someone who cannot edit the trip: then it is a label, and a stop without
|
||||
* a stay shows nothing at all rather than an invitation that leads nowhere.
|
||||
*/
|
||||
/**
|
||||
* How far the road stops short of the place, in the same two-part shell the stay wears.
|
||||
*
|
||||
* Beside the stay rather than under it: both answer "what does this stop cost you", one
|
||||
* in time and one in a walk, and two badges on one line read as one fact about the stop
|
||||
* instead of two unrelated notes stacked up.
|
||||
*/
|
||||
function OffRoadBadge({ meters }: { meters: number }): React.ReactElement {
|
||||
const { t } = useTranslation()
|
||||
const distanceUnit = useSettingsStore(s => s.settings.distance_unit)
|
||||
return (
|
||||
<span
|
||||
className="inline-flex h-[16px] items-stretch self-start overflow-hidden rounded border border-edge"
|
||||
title={t('roadtrip.stop.offRoad', { distance: formatDistance(meters / 1000, distanceUnit) })}
|
||||
>
|
||||
<span
|
||||
className="flex items-center bg-surface-tertiary px-1 text-content-faint"
|
||||
style={{ fontSize: FS.micro }}
|
||||
>
|
||||
<Footprints size={9} aria-hidden />
|
||||
</span>
|
||||
<span
|
||||
className="flex items-center border-s border-edge bg-surface-card px-1.5 font-semibold tabular-nums text-content-secondary"
|
||||
style={{ fontSize: FS.label }}
|
||||
>
|
||||
{formatDistance(meters / 1000, distanceUnit)}
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
function StayBadge({ minutes, onEdit }: { minutes: number | null; onEdit?: () => void }): React.ReactElement | null {
|
||||
const { t } = useTranslation()
|
||||
const text = minutes ? formatDurationShort(minutes * 60) : null
|
||||
@@ -591,19 +622,14 @@ function Stop({ stop, number, entry, late, selected, continues, onSelect, onMove
|
||||
</span>
|
||||
{/* Two halves under one border: the word says what the number means, so the
|
||||
number needs no unit of explanation beside it. */}
|
||||
<StayBadge minutes={stop.dwellMinutes} onEdit={onEditStay} />
|
||||
{/* Only when the walk is far enough to change the plan. A dashed line on the
|
||||
map already says there is a gap; the number is for the case where the gap
|
||||
means luggage, a gate or a track a hire car should not be on. */}
|
||||
{spurWorthLabelling(stop.offRoadMeters) ? (
|
||||
<span
|
||||
className="mt-0.5 inline-flex w-fit items-center gap-1 self-start text-content-faint"
|
||||
style={{ fontSize: FS.label }}
|
||||
>
|
||||
<Footprints size={10} className="shrink-0" aria-hidden />
|
||||
{t('roadtrip.stop.offRoad', { distance: formatDistance((stop.offRoadMeters ?? 0) / 1000, distanceUnit) })}
|
||||
</span>
|
||||
) : null}
|
||||
{/* One row, wrapping: the stay and the walk from the road are both answers to
|
||||
"what does this stop cost", and the second only appears when the gap is far
|
||||
enough to change the plan. The dashed line on the map already says there is
|
||||
one; the number is for luggage, a gate, a track a hire car should not be on. */}
|
||||
<span className="flex flex-wrap items-center gap-1">
|
||||
<StayBadge minutes={stop.dwellMinutes} onEdit={onEditStay} />
|
||||
{spurWorthLabelling(stop.offRoadMeters) ? <OffRoadBadge meters={stop.offRoadMeters ?? 0} /> : null}
|
||||
</span>
|
||||
{lateText ? (
|
||||
<span
|
||||
dir="ltr"
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '{time} فوق أطول قيادة لديك',
|
||||
'roadtrip.limit.dayOver': '{time} زيادة',
|
||||
'roadtrip.limit.range': '{distance} منذ آخر تعبئة',
|
||||
'roadtrip.limit.none': 'غير محددة',
|
||||
'roadtrip.poi.rest': 'استراحة',
|
||||
'roadtrip.poi.campsite': 'مخيم',
|
||||
'roadtrip.poi.food': 'طعام',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '{time} acima do seu trecho máximo',
|
||||
'roadtrip.limit.dayOver': '{time} a mais',
|
||||
'roadtrip.limit.range': '{distance} desde o último abastecimento',
|
||||
'roadtrip.limit.none': 'Nenhum definido',
|
||||
'roadtrip.poi.rest': 'Área de descanso',
|
||||
'roadtrip.poi.campsite': 'Camping',
|
||||
'roadtrip.poi.food': 'Comida',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '{time} per sobre del teu trajecte màxim',
|
||||
'roadtrip.limit.dayOver': '{time} de més',
|
||||
'roadtrip.limit.range': '{distance} des de l’última càrrega',
|
||||
'roadtrip.limit.none': 'Cap definit',
|
||||
'roadtrip.poi.rest': 'Àrea de descans',
|
||||
'roadtrip.poi.campsite': 'Càmping',
|
||||
'roadtrip.poi.food': 'Menjar',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': 'o {time} déle než vaše nejdelší jízda',
|
||||
'roadtrip.limit.dayOver': 'o {time} více',
|
||||
'roadtrip.limit.range': '{distance} od posledního tankování',
|
||||
'roadtrip.limit.none': 'Nenastaveno',
|
||||
'roadtrip.poi.rest': 'Odpočívadlo',
|
||||
'roadtrip.poi.campsite': 'Kemp',
|
||||
'roadtrip.poi.food': 'Jídlo',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '{time} über deiner längsten Fahrt',
|
||||
'roadtrip.limit.dayOver': '{time} drüber',
|
||||
'roadtrip.limit.range': '{distance} seit dem letzten Tanken',
|
||||
'roadtrip.limit.none': 'Keine gesetzt',
|
||||
'roadtrip.poi.rest': 'Rastplatz',
|
||||
'roadtrip.poi.campsite': 'Campingplatz',
|
||||
'roadtrip.poi.food': 'Essen',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '{time} over your longest drive',
|
||||
'roadtrip.limit.dayOver': '{time} over',
|
||||
'roadtrip.limit.range': '{distance} since the last fill-up',
|
||||
'roadtrip.limit.none': 'None set',
|
||||
'roadtrip.poi.rest': 'Rest area',
|
||||
'roadtrip.poi.campsite': 'Campsite',
|
||||
'roadtrip.poi.food': 'Food',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '{time} más que tu trayecto máximo',
|
||||
'roadtrip.limit.dayOver': '{time} de más',
|
||||
'roadtrip.limit.range': '{distance} desde el último repostaje',
|
||||
'roadtrip.limit.none': 'Ninguno definido',
|
||||
'roadtrip.poi.rest': 'Área de descanso',
|
||||
'roadtrip.poi.campsite': 'Camping',
|
||||
'roadtrip.poi.food': 'Comida',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '{time} de plus que votre trajet maximal',
|
||||
'roadtrip.limit.dayOver': '{time} de trop',
|
||||
'roadtrip.limit.range': '{distance} depuis le dernier plein',
|
||||
'roadtrip.limit.none': 'Aucune définie',
|
||||
'roadtrip.poi.rest': 'Aire de repos',
|
||||
'roadtrip.poi.campsite': 'Camping',
|
||||
'roadtrip.poi.food': 'Restauration',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '{time} πάνω από τη μέγιστη οδήγησή σας',
|
||||
'roadtrip.limit.dayOver': '{time} πάνω',
|
||||
'roadtrip.limit.range': '{distance} από τον τελευταίο ανεφοδιασμό',
|
||||
'roadtrip.limit.none': 'Δεν έχουν οριστεί',
|
||||
'roadtrip.poi.rest': 'Χώρος ανάπαυσης',
|
||||
'roadtrip.poi.campsite': 'Κάμπινγκ',
|
||||
'roadtrip.poi.food': 'Φαγητό',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '{time} a leghosszabb vezetésed felett',
|
||||
'roadtrip.limit.dayOver': '{time} túl',
|
||||
'roadtrip.limit.range': '{distance} az utolsó tankolás óta',
|
||||
'roadtrip.limit.none': 'Nincs beállítva',
|
||||
'roadtrip.poi.rest': 'Pihenőhely',
|
||||
'roadtrip.poi.campsite': 'Kemping',
|
||||
'roadtrip.poi.food': 'Étkezés',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '{time} lebih dari perjalanan terlama',
|
||||
'roadtrip.limit.dayOver': 'lebih {time}',
|
||||
'roadtrip.limit.range': '{distance} sejak pengisian terakhir',
|
||||
'roadtrip.limit.none': 'Belum diatur',
|
||||
'roadtrip.poi.rest': 'Tempat istirahat',
|
||||
'roadtrip.poi.campsite': 'Perkemahan',
|
||||
'roadtrip.poi.food': 'Makanan',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '{time} oltre il tuo tratto massimo',
|
||||
'roadtrip.limit.dayOver': '{time} in più',
|
||||
'roadtrip.limit.range': '{distance} dall’ultimo rifornimento',
|
||||
'roadtrip.limit.none': 'Nessuno impostato',
|
||||
'roadtrip.poi.rest': 'Area di sosta',
|
||||
'roadtrip.poi.campsite': 'Campeggio',
|
||||
'roadtrip.poi.food': 'Ristoro',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '連続運転の上限を{time}超過',
|
||||
'roadtrip.limit.dayOver': '{time}超過',
|
||||
'roadtrip.limit.range': '前回の給油から{distance}',
|
||||
'roadtrip.limit.none': '未設定',
|
||||
'roadtrip.poi.rest': 'サービスエリア',
|
||||
'roadtrip.poi.campsite': 'キャンプ場',
|
||||
'roadtrip.poi.food': '食事',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '연속 운전 한도 {time} 초과',
|
||||
'roadtrip.limit.dayOver': '{time} 초과',
|
||||
'roadtrip.limit.range': '마지막 주유 이후 {distance}',
|
||||
'roadtrip.limit.none': '설정 안 됨',
|
||||
'roadtrip.poi.rest': '휴게소',
|
||||
'roadtrip.poi.campsite': '캠핑장',
|
||||
'roadtrip.poi.food': '식사',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '{time} boven je langste rit',
|
||||
'roadtrip.limit.dayOver': '{time} te veel',
|
||||
'roadtrip.limit.range': '{distance} sinds de laatste tankbeurt',
|
||||
'roadtrip.limit.none': 'Geen ingesteld',
|
||||
'roadtrip.poi.rest': 'Rustplaats',
|
||||
'roadtrip.poi.campsite': 'Camping',
|
||||
'roadtrip.poi.food': 'Eten',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '{time} ponad twoją najdłuższą jazdę',
|
||||
'roadtrip.limit.dayOver': '{time} ponad',
|
||||
'roadtrip.limit.range': '{distance} od ostatniego tankowania',
|
||||
'roadtrip.limit.none': 'Brak ustawionych',
|
||||
'roadtrip.poi.rest': 'Miejsce odpoczynku',
|
||||
'roadtrip.poi.campsite': 'Kemping',
|
||||
'roadtrip.poi.food': 'Jedzenie',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': 'на {time} дольше вашего перегона',
|
||||
'roadtrip.limit.dayOver': 'на {time} больше',
|
||||
'roadtrip.limit.range': '{distance} с последней заправки',
|
||||
'roadtrip.limit.none': 'Не заданы',
|
||||
'roadtrip.poi.rest': 'Зона отдыха',
|
||||
'roadtrip.poi.campsite': 'Кемпинг',
|
||||
'roadtrip.poi.food': 'Еда',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '{time} över din längsta körning',
|
||||
'roadtrip.limit.dayOver': '{time} över',
|
||||
'roadtrip.limit.range': '{distance} sedan senaste tankningen',
|
||||
'roadtrip.limit.none': 'Inga angivna',
|
||||
'roadtrip.poi.rest': 'Rastplats',
|
||||
'roadtrip.poi.campsite': 'Campingplats',
|
||||
'roadtrip.poi.food': 'Mat',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': 'en uzun sürüşünüzden {time} fazla',
|
||||
'roadtrip.limit.dayOver': '{time} fazla',
|
||||
'roadtrip.limit.range': 'son yakıt alımından beri {distance}',
|
||||
'roadtrip.limit.none': 'Ayarlanmadı',
|
||||
'roadtrip.poi.rest': 'Dinlenme alanı',
|
||||
'roadtrip.poi.campsite': 'Kamp alanı',
|
||||
'roadtrip.poi.food': 'Yemek',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': 'на {time} довше вашого перегону',
|
||||
'roadtrip.limit.dayOver': 'на {time} більше',
|
||||
'roadtrip.limit.range': '{distance} від останньої заправки',
|
||||
'roadtrip.limit.none': 'Не задано',
|
||||
'roadtrip.poi.rest': 'Зона відпочинку',
|
||||
'roadtrip.poi.campsite': 'Кемпінг',
|
||||
'roadtrip.poi.food': 'Їжа',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': 'vượt {time} so với chặng dài nhất',
|
||||
'roadtrip.limit.dayOver': 'vượt {time}',
|
||||
'roadtrip.limit.range': '{distance} kể từ lần đổ nhiên liệu cuối',
|
||||
'roadtrip.limit.none': 'Chưa đặt',
|
||||
'roadtrip.poi.rest': 'Trạm dừng nghỉ',
|
||||
'roadtrip.poi.campsite': 'Khu cắm trại',
|
||||
'roadtrip.poi.food': 'Đồ ăn',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '超出最長駕駛 {time}',
|
||||
'roadtrip.limit.dayOver': '超出 {time}',
|
||||
'roadtrip.limit.range': '距上次加油 {distance}',
|
||||
'roadtrip.limit.none': '未設定',
|
||||
'roadtrip.poi.rest': '休息站',
|
||||
'roadtrip.poi.campsite': '露營地',
|
||||
'roadtrip.poi.food': '餐飲',
|
||||
|
||||
@@ -29,6 +29,7 @@ const roadtrip: TranslationStrings = {
|
||||
'roadtrip.limit.legOver': '超出最长驾驶 {time}',
|
||||
'roadtrip.limit.dayOver': '超出 {time}',
|
||||
'roadtrip.limit.range': '距上次加油 {distance}',
|
||||
'roadtrip.limit.none': '未设置',
|
||||
'roadtrip.poi.rest': '服务区',
|
||||
'roadtrip.poi.campsite': '露营地',
|
||||
'roadtrip.poi.food': '餐饮',
|
||||
|
||||
Reference in New Issue
Block a user