feat(journey): make the linked trips' GPX tracks a switch, off by default

#1260 drew the GPX tracks of every trip behind a journey onto its map, with
nothing to switch off — the reasoning being that a route imported into a
linked trip is part of the journey's story. For a trip carrying a season of
recorded drives it is instead a map nobody asked for, drawn from places that
never became an entry.

So it becomes a journey-level setting, owner-only like the other Journey
Settings, and it starts off: a journal should show what its author put in it,
and #1260's set is everything the linked trips happen to contain. The switch
gates the request, not just the rendering — the endpoint ships full unthinned
geometry, which is megabytes fetched on every journey open for a map nobody
wanted.

`journeyTracks()` itself is deliberately untouched, so TREK Studio keeps the
set its saved books were laid out with.

Also fixes what the reporter's screenshot actually shows: JourneyMap fed track
coordinates into `fitBounds`, so one drive across a country zoomed the journey
map out until the entries were specks. Tracks now only frame the map when
there is nothing else to frame, which is what JourneyMapGL already did — the
same journey used to open at two different zooms depending on the renderer.

Closes #2194

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S5VwmiBQ9sPCJwfmpv64r3
This commit is contained in:
Claude
2026-09-02 12:01:14 +00:00
committed by Maurice
parent 5e5fe288d8
commit b7f9c1e8ae
31 changed files with 211 additions and 13 deletions
@@ -77,6 +77,21 @@ export function JourneySettingsDialog({ journey, onClose, onSaved, onOpenInvite,
}
}
// Saved on the spot rather than on Save, like the archive switch above it:
// it is a view setting, and the point of it is seeing the map change (#2194).
const [savingTracks, setSavingTracks] = useState(false)
const handleTracksToggle = async () => {
setSavingTracks(true)
try {
await updateJourney(journey.id, { show_trip_tracks: !journey.show_trip_tracks })
onSaved()
} catch {
toast.error(t('journey.settings.saveFailed'))
} finally {
setSavingTracks(false)
}
}
const handleDelete = async () => {
try {
await deleteJourney(journey.id)
@@ -141,6 +156,30 @@ export function JourneySettingsDialog({ journey, onClose, onSaved, onOpenInvite,
/>
</div>
{/* Trip GPX tracks on the journey map (#2194) */}
<div>
<label className="text-[10px] font-semibold tracking-[0.12em] uppercase text-zinc-500 block mb-1.5">{t('journey.settings.tracks')}</label>
<button
type="button"
role="switch"
aria-checked={!!journey.show_trip_tracks}
disabled={savingTracks}
onClick={handleTracksToggle}
className="w-full flex items-center gap-3 px-3.5 py-2.5 border border-zinc-200 dark:border-zinc-700 rounded-xl bg-white dark:bg-zinc-800 text-left disabled:opacity-60"
>
<span className="flex-1 min-w-0">
<span className="block text-[14px] text-zinc-900 dark:text-white">{t('journey.settings.showTripTracks')}</span>
<span className="block text-[11px] text-zinc-500">{t('journey.settings.showTripTracksHint')}</span>
</span>
<span
aria-hidden="true"
className={`w-9 h-5 rounded-full flex-shrink-0 p-0.5 transition-colors ${journey.show_trip_tracks ? 'bg-emerald-500' : 'bg-zinc-300 dark:bg-zinc-600'}`}
>
<span className={`block w-4 h-4 rounded-full bg-white transition-transform ${journey.show_trip_tracks ? 'translate-x-4' : ''}`} />
</span>
</button>
</div>
</div>
{/* Right column */}
+18 -3
View File
@@ -268,6 +268,18 @@ function JourneyMap(
itemsRef.current = items
const allCoords: L.LatLngTuple[] = []
/**
* Track geometry is kept OUT of the fit set (#2194) and used only when there
* is nothing else to frame.
*
* A journey's opening viewport should show the journey — its entries and the
* trail between them. Letting a recorded GPX into the bounds meant one drive
* across a country zoomed the map out until the entries were specks, which
* is what the reporter's screenshot shows. JourneyMapGL already fits on
* entries + trail alone (JourneyMapGL.tsx), so this also ends an asymmetry
* where the same journey framed differently per renderer.
*/
const trackCoords: L.LatLngTuple[] = []
if (stableTrail.length > 1) {
const coords = stableTrail.map(p => [p.lat, p.lng] as L.LatLngTuple)
@@ -293,7 +305,7 @@ function JourneyMap(
// becomes innerHTML, and a track name is a place name off a shared trip.
if (track.name) line.bindTooltip(escapeHtml(track.name), { sticky: true, direction: 'top', className: 'map-tooltip' })
line.addTo(map)
coords.forEach(c => allCoords.push(c))
coords.forEach(c => trackCoords.push(c))
}
// route polyline — only in non-fullscreen (sidebar map) mode
@@ -341,9 +353,12 @@ function JourneyMap(
if (!mapRef.current) return
try {
map.invalidateSize()
if (allCoords.length > 0) {
// Tracks only get a say when the journey has nothing located of its own —
// a world view would be worse than framing the one thing on the map.
const fitCoords = allCoords.length > 0 ? allCoords : trackCoords
if (fitCoords.length > 0) {
const pb = paddingBottom || 50
map.fitBounds(L.latLngBounds(allCoords), { paddingTopLeft: [50, 50], paddingBottomRight: [50, pb], maxZoom: 16 })
map.fitBounds(L.latLngBounds(fitCoords), { paddingTopLeft: [50, 50], paddingBottomRight: [50, pb], maxZoom: 16 })
} else {
map.setView([30, 0], 2)
}
@@ -13,6 +13,9 @@ import { lockBodyScroll } from '../../utils/bodyScrollLock'
import type { JourneyEntry } from '../../store/journeyStore'
import { createDraftJourneyEntry } from './JourneyDetailPage.helpers'
/** Stable identity for "this journey draws no trip tracks" (#2194). */
const NO_TRACKS: JourneyTrack[] = []
/**
* Journey detail page logic — owns the journey load + WebSocket live sync, the
* timeline/gallery view state, the entry editor/viewer/delete/lightbox dialogs,
@@ -71,18 +74,31 @@ export function useJourneyDetail() {
if (id) loadJourney(Number(id)).catch(() => {})
}, [id])
// GPX tracks of the trips behind this journey (#1260). Loaded separately from the
// journey itself: a journey without tracks is the common case, and a failed lookup
// should cost the map its lines, not the whole page.
const [tracks, setTracks] = useState<JourneyTrack[]>([])
// GPX tracks of the trips behind this journey (#1260), drawn only when the
// journey asks for them (#2194). Loaded separately from the journey itself: a
// journey without tracks is the common case, and a failed lookup should cost
// the map its lines, not the whole page.
//
// The switch gates the REQUEST, not just the rendering: the endpoint ships
// full unthinned geometry, which for a trip carrying a season of recorded
// drives is megabytes fetched on every journey open for a map nobody asked
// for. It stays untouched on the server so TREK Studio, which freezes the
// same endpoint's output into a saved book, keeps the set it laid out with.
const [tracks, setTracks] = useState<JourneyTrack[]>(NO_TRACKS)
const showTripTracks = !!current?.show_trip_tracks
useEffect(() => {
if (!id) return
if (!id || !showTripTracks) {
// A stable empty array: a fresh literal here would be a new reference on
// every toggle and re-render the map for nothing.
setTracks(NO_TRACKS)
return
}
let cancelled = false
journeyApi.listTracks(Number(id))
.then(res => { if (!cancelled) setTracks(res.tracks ?? []) })
.catch(() => { if (!cancelled) setTracks([]) })
.then(res => { if (!cancelled) setTracks(res.tracks ?? NO_TRACKS) })
.catch(() => { if (!cancelled) setTracks(NO_TRACKS) })
return () => { cancelled = true }
}, [id])
}, [id, showTripTracks])
useEffect(() => {
if (current?.hide_skeletons !== undefined) setHideSkeletons(current.hide_skeletons)
+6
View File
@@ -11,6 +11,12 @@ export interface Journey {
cover_gradient?: string | null
cover_image?: string | null
status: 'draft' | 'active' | 'completed' | 'archived'
/**
* Draw the GPX tracks of the journey's linked trips on its map (#2194).
* Off unless the owner asks for it — see the migration for why the default
* flipped. Optional so a cached pre-#2194 journey still parses.
*/
show_trip_tracks?: boolean
created_at: number
updated_at: number
}
+23
View File
@@ -4191,6 +4191,29 @@ function runMigrations(db: Database.Database): void {
() => {
db.prepare("UPDATE addons SET type = 'integration' WHERE id = 'naver_list_import'").run();
},
/**
* Whether a journey's map draws the GPX tracks of the trips behind it (#2194).
*
* #1260 added those tracks unconditionally and with nothing to switch off,
* on the reasoning that a route imported into a linked trip is part of the
* journey's story. For a trip carrying a season of recorded drives it is
* instead a map nobody asked for, drawn from places that never became an
* entry — so it becomes a journey-level setting.
*
* DEFAULT 0, i.e. off: the tracks are opt-in from here on. That is a
* deliberate behaviour change rather than a preserved default — a journal
* should show what its author put in it, and #1260's set is everything the
* linked trips happen to contain. Owners who want them back have one
* switch in Journey Settings.
*
* Appended LAST: the array is index-addressed against schema_version.
*/
() => {
const cols = db.prepare("SELECT name FROM pragma_table_info('journeys')").all() as Array<{ name: string }>;
if (!cols.some(c => c.name === 'show_trip_tracks')) {
db.exec('ALTER TABLE journeys ADD COLUMN show_trip_tracks INTEGER NOT NULL DEFAULT 0');
}
},
];
if (currentVersion < migrations.length) {
@@ -329,6 +329,7 @@ export class JourneyDomainService {
cover_gradient: string;
cover_image: string;
status: string;
show_trip_tracks: boolean | number;
}>,
): Journey | null {
// Journey-level settings (title, cover, status) are owner-only — editors
@@ -336,14 +337,17 @@ export class JourneyDomainService {
if (!this.isOwner(journeyId, userId)) return null;
const ALLOWED_STATUSES = ['draft', 'active', 'completed', 'archived'];
const allowed = ['title', 'subtitle', 'cover_gradient', 'cover_image', 'status'];
const allowed = ['title', 'subtitle', 'cover_gradient', 'cover_image', 'status', 'show_trip_tracks'];
// Stored as INTEGER, and better-sqlite3 refuses to bind a JS boolean, so the
// one flag on this table is coerced rather than passed through.
const BOOLEAN_FIELDS = new Set(['show_trip_tracks']);
const fields: string[] = [];
const values: unknown[] = [];
for (const [key, val] of Object.entries(data)) {
if (val !== undefined && allowed.includes(key)) {
if (key === 'status' && !ALLOWED_STATUSES.includes(val as string)) continue;
fields.push(`${key} = ?`);
values.push(val);
values.push(BOOLEAN_FIELDS.has(key) ? (val ? 1 : 0) : val);
}
}
if (fields.length === 0) return this.db.prepare('SELECT * FROM journeys WHERE id = ?').get(journeyId) as Journey;
+2
View File
@@ -356,6 +356,8 @@ export interface Journey {
cover_gradient?: string | null;
cover_image?: string | null;
status: 'draft' | 'active' | 'completed' | 'archived';
/** Draw the linked trips' GPX tracks on this journey's map (#2194). 0 by default. */
show_trip_tracks?: number;
created_at: number;
updated_at: number;
}
@@ -356,6 +356,30 @@ describe('updateJourney', () => {
expect(result).not.toBeNull();
expect(result!.status).toBe('active');
});
it('JOURNEY-SVC-021d: trip GPX tracks are off until the owner switches them on (#2194)', () => {
const { user } = createUser(testDb);
const journey = createJourney(testDb, user.id, { title: 'Norway' });
// #1260 drew the linked trips' tracks unconditionally; they are opt-in now.
expect(svc.updateJourney(journey.id, user.id, {})!.show_trip_tracks).toBe(0);
// A JS boolean has to survive the trip into an INTEGER column —
// better-sqlite3 refuses to bind one, so this would throw uncoerced.
expect(svc.updateJourney(journey.id, user.id, { show_trip_tracks: true })!.show_trip_tracks).toBe(1);
expect(svc.updateJourney(journey.id, user.id, { show_trip_tracks: false })!.show_trip_tracks).toBe(0);
});
it('JOURNEY-SVC-021e: an editor cannot switch the trip tracks on (#2194)', () => {
const { user } = createUser(testDb);
const { user: editor } = createUser(testDb, { username: 'tracks-editor' });
const journey = createJourney(testDb, user.id, { title: 'Norway' });
testDb.prepare("INSERT INTO journey_contributors (journey_id, user_id, role, added_at) VALUES (?, ?, 'editor', 0)")
.run(journey.id, editor.id);
// Journey-level settings stay owner-only, same as title and status.
expect(svc.updateJourney(journey.id, editor.id, { show_trip_tracks: true })).toBeNull();
});
});
describe('deleteJourney', () => {
+3
View File
@@ -216,6 +216,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Name', // en-fallback
'journey.settings.subtitle': 'Subtitle', // en-fallback
'journey.settings.subtitlePlaceholder': 'e.g. Thailand, Vietnam & Cambodia', // en-fallback
'journey.settings.tracks': 'مسارات GPX',
'journey.settings.showTripTracks': 'إظهار جميع مسارات GPX للرحلات',
'journey.settings.showTripTracksHint': 'يرسم على الخريطة المسارات المسجَّلة من الرحلات المرتبطة.',
'journey.settings.delete': 'Delete', // en-fallback
'journey.settings.deleteJourney': 'Delete Journey', // en-fallback
'journey.settings.deleteMessage': 'Delete "{title}"? All entries and photos will be lost.', // en-fallback
+3
View File
@@ -190,6 +190,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Nome',
'journey.settings.subtitle': 'Subtítulo',
'journey.settings.subtitlePlaceholder': 'ex. Tailândia, Vietnã e Camboja',
'journey.settings.tracks': 'Trilhas GPX',
'journey.settings.showTripTracks': 'Mostrar todas as trilhas GPX das viagens',
'journey.settings.showTripTracksHint': 'Desenha no mapa as rotas gravadas das viagens vinculadas.',
'journey.settings.endJourney': 'Arquivar Jornada',
'journey.settings.reopenJourney': 'Restaurar Jornada',
'journey.settings.archived': 'Jornada arquivada',
+3
View File
@@ -190,6 +190,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Nom',
'journey.settings.subtitle': 'Subtítol',
'journey.settings.subtitlePlaceholder': 'p. ex. Tailàndia, Vietnam i Cambodja',
'journey.settings.tracks': 'Traces GPX',
'journey.settings.showTripTracks': 'Mostra totes les traces GPX dels viatges',
'journey.settings.showTripTracksHint': 'Dibuixa al mapa les rutes enregistrades dels viatges enllaçats.',
'journey.settings.endJourney': 'Arxiva el viatge',
'journey.settings.reopenJourney': 'Restaura el viatge',
'journey.settings.archived': 'Viatge arxivat',
+3
View File
@@ -190,6 +190,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Název',
'journey.settings.subtitle': 'Podtitul',
'journey.settings.subtitlePlaceholder': 'např. Thajsko, Vietnam a Kambodža',
'journey.settings.tracks': 'Stopy GPX',
'journey.settings.showTripTracks': 'Zobrazit všechny GPX stopy z cest',
'journey.settings.showTripTracksHint': 'Vykreslí na mapě zaznamenané trasy z propojených cest.',
'journey.settings.endJourney': 'Archivovat cestu',
'journey.settings.reopenJourney': 'Obnovit cestu',
'journey.settings.archived': 'Cesta archivována',
+3
View File
@@ -194,6 +194,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Name',
'journey.settings.subtitle': 'Untertitel',
'journey.settings.subtitlePlaceholder': 'z.B. Thailand, Vietnam & Kambodscha',
'journey.settings.tracks': 'GPX-Tracks',
'journey.settings.showTripTracks': 'Alle GPX-Tracks der Reisen anzeigen',
'journey.settings.showTripTracksHint': 'Zeichnet die aufgezeichneten Routen der verknüpften Reisen auf die Karte.',
'journey.settings.endJourney': 'Reise archivieren',
'journey.settings.reopenJourney': 'Reise wiederherstellen',
'journey.settings.archived': 'Reise archiviert',
+3
View File
@@ -202,6 +202,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Name',
'journey.settings.subtitle': 'Subtitle',
'journey.settings.subtitlePlaceholder': 'e.g. Thailand, Vietnam & Cambodia',
'journey.settings.tracks': 'GPX Tracks',
'journey.settings.showTripTracks': 'Show all trip GPX tracks',
'journey.settings.showTripTracksHint': 'Draws the recorded routes from the linked trips on the map.',
'journey.settings.endJourney': 'Archive Journey',
'journey.settings.reopenJourney': 'Restore Journey',
'journey.settings.archived': 'Journey archived',
+3
View File
@@ -190,6 +190,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Nombre',
'journey.settings.subtitle': 'Subtítulo',
'journey.settings.subtitlePlaceholder': 'p. ej. Tailandia, Vietnam y Camboya',
'journey.settings.tracks': 'Rutas GPX',
'journey.settings.showTripTracks': 'Mostrar todas las rutas GPX de los viajes',
'journey.settings.showTripTracksHint': 'Dibuja en el mapa las rutas grabadas de los viajes vinculados.',
'journey.settings.endJourney': 'Archivar viaje',
'journey.settings.reopenJourney': 'Restaurar viaje',
'journey.settings.archived': 'Viaje archivado',
+3
View File
@@ -191,6 +191,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Nom',
'journey.settings.subtitle': 'Sous-titre',
'journey.settings.subtitlePlaceholder': 'ex. Thaïlande, Vietnam et Cambodge',
'journey.settings.tracks': 'Traces GPX',
'journey.settings.showTripTracks': 'Afficher toutes les traces GPX des voyages',
'journey.settings.showTripTracksHint': 'Trace sur la carte les itinéraires enregistrés des voyages liés.',
'journey.settings.endJourney': 'Archiver le journal',
'journey.settings.reopenJourney': 'Restaurer le journal',
'journey.settings.archived': 'Journal archivé',
+3
View File
@@ -197,6 +197,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Όνομα',
'journey.settings.subtitle': 'Υπότιτλος',
'journey.settings.subtitlePlaceholder': 'π.χ. Ταϊλάνδη, Βιετνάμ & Καμπότζη',
'journey.settings.tracks': 'Διαδρομές GPX',
'journey.settings.showTripTracks': 'Εμφάνιση όλων των διαδρομών GPX των ταξιδιών',
'journey.settings.showTripTracksHint': 'Σχεδιάζει στον χάρτη τις καταγεγραμμένες διαδρομές από τα συνδεδεμένα ταξίδια.',
'journey.settings.endJourney': 'Αρχειοθέτηση Ταξιδιού',
'journey.settings.reopenJourney': 'Επαναφορά Ταξιδιού',
'journey.settings.archived': 'Το ταξίδι αρχειοθετήθηκε',
+3
View File
@@ -190,6 +190,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Név',
'journey.settings.subtitle': 'Alcím',
'journey.settings.subtitlePlaceholder': 'pl. Thaiföld, Vietnam és Kambodzsa',
'journey.settings.tracks': 'GPX-nyomvonalak',
'journey.settings.showTripTracks': 'Az utazások összes GPX-nyomvonalának megjelenítése',
'journey.settings.showTripTracksHint': 'A kapcsolt utazások rögzített útvonalait rajzolja a térképre.',
'journey.settings.endJourney': 'Út archiválása',
'journey.settings.reopenJourney': 'Út visszaállítása',
'journey.settings.archived': 'Út archiválva',
+3
View File
@@ -189,6 +189,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Nama',
'journey.settings.subtitle': 'Subjudul',
'journey.settings.subtitlePlaceholder': 'mis. Thailand, Vietnam & Kamboja',
'journey.settings.tracks': 'Jalur GPX',
'journey.settings.showTripTracks': 'Tampilkan semua jalur GPX perjalanan',
'journey.settings.showTripTracksHint': 'Menggambar rute terekam dari perjalanan yang tertaut pada peta.',
'journey.settings.endJourney': 'Arsipkan Perjalanan',
'journey.settings.reopenJourney': 'Pulihkan Perjalanan',
'journey.settings.archived': 'Perjalanan diarsipkan',
+3
View File
@@ -190,6 +190,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Nome',
'journey.settings.subtitle': 'Sottotitolo',
'journey.settings.subtitlePlaceholder': 'es. Thailandia, Vietnam e Cambogia',
'journey.settings.tracks': 'Tracce GPX',
'journey.settings.showTripTracks': 'Mostra tutte le tracce GPX dei viaggi',
'journey.settings.showTripTracksHint': 'Disegna sulla mappa i percorsi registrati dei viaggi collegati.',
'journey.settings.endJourney': 'Archivia il viaggio',
'journey.settings.reopenJourney': 'Ripristina il viaggio',
'journey.settings.archived': 'Viaggio archiviato',
+3
View File
@@ -192,6 +192,9 @@ const journey: TranslationStrings = {
'journey.settings.name': '名前',
'journey.settings.subtitle': 'サブタイトル',
'journey.settings.subtitlePlaceholder': '例:タイ・ベトナム・カンボジア',
'journey.settings.tracks': 'GPXトラック',
'journey.settings.showTripTracks': '旅行のGPXトラックをすべて表示',
'journey.settings.showTripTracksHint': 'リンクされた旅行の記録済みルートを地図に描画します。',
'journey.settings.endJourney': '日記をアーカイブ',
'journey.settings.reopenJourney': '日記を復元',
'journey.settings.archived': '日記をアーカイブしました',
+3
View File
@@ -191,6 +191,9 @@ const journey: TranslationStrings = {
'journey.settings.name': '이름',
'journey.settings.subtitle': '부제목',
'journey.settings.subtitlePlaceholder': '예: 태국, 베트남 & 캄보디아',
'journey.settings.tracks': 'GPX 트랙',
'journey.settings.showTripTracks': '여행의 모든 GPX 트랙 표시',
'journey.settings.showTripTracksHint': '연결된 여행의 기록된 경로를 지도에 그립니다.',
'journey.settings.endJourney': 'Journey 보관',
'journey.settings.reopenJourney': 'Journey 복원',
'journey.settings.archived': 'Journey가 보관되었습니다',
+3
View File
@@ -191,6 +191,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Naam',
'journey.settings.subtitle': 'Ondertitel',
'journey.settings.subtitlePlaceholder': 'bijv. Thailand, Vietnam & Cambodja',
'journey.settings.tracks': 'GPX-tracks',
'journey.settings.showTripTracks': 'Alle GPX-tracks van reizen tonen',
'journey.settings.showTripTracksHint': 'Tekent de opgenomen routes van de gekoppelde reizen op de kaart.',
'journey.settings.endJourney': 'Reis archiveren',
'journey.settings.reopenJourney': 'Reis herstellen',
'journey.settings.archived': 'Reis gearchiveerd',
+3
View File
@@ -190,6 +190,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Nazwa',
'journey.settings.subtitle': 'Podtytuł',
'journey.settings.subtitlePlaceholder': 'np. Tajlandia, Wietnam i Kambodża',
'journey.settings.tracks': 'Ślady GPX',
'journey.settings.showTripTracks': 'Pokaż wszystkie ślady GPX z podróży',
'journey.settings.showTripTracksHint': 'Rysuje na mapie zapisane trasy z powiązanych podróży.',
'journey.settings.endJourney': 'Archiwizuj podróż',
'journey.settings.reopenJourney': 'Przywróć podróż',
'journey.settings.archived': 'Podróż zarchiwizowana',
+3
View File
@@ -190,6 +190,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Название',
'journey.settings.subtitle': 'Подзаголовок',
'journey.settings.subtitlePlaceholder': 'напр. Таиланд, Вьетнам и Камбоджа',
'journey.settings.tracks': 'GPX-треки',
'journey.settings.showTripTracks': 'Показывать все GPX-треки поездок',
'journey.settings.showTripTracksHint': 'Рисует на карте записанные маршруты из связанных поездок.',
'journey.settings.endJourney': 'Архивировать путешествие',
'journey.settings.reopenJourney': 'Восстановить путешествие',
'journey.settings.archived': 'Путешествие архивировано',
+3
View File
@@ -195,6 +195,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Namn',
'journey.settings.subtitle': 'Undertext',
'journey.settings.subtitlePlaceholder': 't.ex. Thailand, Vietnam och Kambodja',
'journey.settings.tracks': 'GPX-spår',
'journey.settings.showTripTracks': 'Visa alla GPX-spår från resor',
'journey.settings.showTripTracksHint': 'Ritar de inspelade rutterna från de länkade resorna på kartan.',
'journey.settings.endJourney': 'Arkivera Journey',
'journey.settings.reopenJourney': 'Återställ Journey',
'journey.settings.archived': 'Journey arkiverad',
+3
View File
@@ -192,6 +192,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'İsim',
'journey.settings.subtitle': 'Alt başlık',
'journey.settings.subtitlePlaceholder': 'örn. Tayland, Vietnam ve Kamboçya',
'journey.settings.tracks': 'GPX İzleri',
'journey.settings.showTripTracks': 'Tüm gezi GPX izlerini göster',
'journey.settings.showTripTracksHint': 'Bağlı gezilerdeki kayıtlı rotaları haritaya çizer.',
'journey.settings.endJourney': "Journey'i Arşivle",
'journey.settings.reopenJourney': "Journey'i geri aç",
'journey.settings.archived': 'Journey arşivlendi',
+3
View File
@@ -192,6 +192,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Назва',
'journey.settings.subtitle': 'Підзаголовок',
'journey.settings.subtitlePlaceholder': 'наприклад Таїланд, В’єтнам і Камбоджа',
'journey.settings.tracks': 'GPX-треки',
'journey.settings.showTripTracks': 'Показувати всі GPX-треки подорожей',
'journey.settings.showTripTracksHint': 'Малює на карті записані маршрути зі зв’язаних подорожей.',
'journey.settings.endJourney': 'Архівувати подорож',
'journey.settings.reopenJourney': 'Відновити подорож',
'journey.settings.archived': 'Подорож архівовано',
+3
View File
@@ -195,6 +195,9 @@ const journey: TranslationStrings = {
'journey.settings.name': 'Tên',
'journey.settings.subtitle': 'Phụ đề',
'journey.settings.subtitlePlaceholder': 'ví dụ. Thái Lan, Việt Nam và Campuchia',
'journey.settings.tracks': 'Tuyến GPX',
'journey.settings.showTripTracks': 'Hiện tất cả tuyến GPX của chuyến đi',
'journey.settings.showTripTracksHint': 'Vẽ lên bản đồ các tuyến đường đã ghi từ những chuyến đi được liên kết.',
'journey.settings.endJourney': 'Hành trình lưu trữ',
'journey.settings.reopenJourney': 'Khôi phục hành trình',
'journey.settings.archived': 'Đã lưu trữ hành trình',
+3
View File
@@ -188,6 +188,9 @@ const journey: TranslationStrings = {
'journey.settings.name': '名稱',
'journey.settings.subtitle': '副標題',
'journey.settings.subtitlePlaceholder': '例如 泰國、越南和柬埔寨',
'journey.settings.tracks': 'GPX 軌跡',
'journey.settings.showTripTracks': '顯示所有行程的 GPX 軌跡',
'journey.settings.showTripTracksHint': '在地圖上繪製關聯行程中的已記錄路線。',
'journey.settings.endJourney': '封存旅程',
'journey.settings.reopenJourney': '還原旅程',
'journey.settings.archived': '旅程已封存',
+3
View File
@@ -188,6 +188,9 @@ const journey: TranslationStrings = {
'journey.settings.name': '名称',
'journey.settings.subtitle': '副标题',
'journey.settings.subtitlePlaceholder': '例如 泰国、越南和柬埔寨',
'journey.settings.tracks': 'GPX 轨迹',
'journey.settings.showTripTracks': '显示所有行程的 GPX 轨迹',
'journey.settings.showTripTracksHint': '在地图上绘制关联行程中的已记录路线。',
'journey.settings.endJourney': '归档旅程',
'journey.settings.reopenJourney': '恢复旅程',
'journey.settings.archived': '旅程已归档',