mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 14:06:27 +00:00
feat(pdf): transport notes in the trip PDF, with a switch in the preview
The notes of a flight, train or rental print under its booking code, like the notes of a place. A Transport notes switch next to Page break per day leaves them out and is remembered; it only shows when a transport has a note. (#1571)
This commit is contained in:
@@ -1363,3 +1363,53 @@ describe('a printed day follows the plan (#1978)', () => {
|
||||
expect(seen).toEqual([...seen].sort((a, b) => a - b))
|
||||
})
|
||||
})
|
||||
|
||||
// ── Transport notes (#1571) ─────────────────────────────────────────────────
|
||||
|
||||
describe('downloadTripPDF — transport notes', () => {
|
||||
const withNote = { ...transportReservation, notes: 'Seat 14A\nCheck in online the day before' }
|
||||
|
||||
beforeEach(() => {
|
||||
localStorage.removeItem('trek_pdf_transport_notes')
|
||||
})
|
||||
|
||||
const notesToggle = () => document.querySelector<HTMLButtonElement>('#pdf-transport-notes-toggle')
|
||||
|
||||
it('FE-PDF-TNOTES-001: prints the note of a transport, escaped, and offers the switch', async () => {
|
||||
await downloadTripPDF({ ...richArgs, reservations: [{ ...withNote, notes: 'Gate <b>B12</b>' }] })
|
||||
const html = getIframe()!.srcdoc
|
||||
|
||||
expect(html).toContain('<div class="note-time transport-note">Gate <b>B12</b></div>')
|
||||
expect(html).not.toContain('pdf-no-transport-notes"')
|
||||
expect(notesToggle()!.getAttribute('aria-checked')).toBe('true')
|
||||
})
|
||||
|
||||
it('FE-PDF-TNOTES-002: without any transport note there is nothing to switch', async () => {
|
||||
await downloadTripPDF(richArgs)
|
||||
|
||||
expect(getIframe()!.srcdoc).not.toContain('class="note-time transport-note"')
|
||||
expect(notesToggle()).toBeNull()
|
||||
// The page break switch is still there on its own.
|
||||
expect(document.querySelector('#pdf-daybreak-toggle')).not.toBeNull()
|
||||
})
|
||||
|
||||
it('FE-PDF-TNOTES-003: turning the notes off hides them at once and is remembered', async () => {
|
||||
await downloadTripPDF({ ...richArgs, reservations: [withNote] })
|
||||
notesToggle()!.click()
|
||||
|
||||
expect(notesToggle()!.getAttribute('aria-checked')).toBe('false')
|
||||
expect(localStorage.getItem('trek_pdf_transport_notes')).toBe('0')
|
||||
const body = getIframe()!.contentDocument?.body
|
||||
if (body) expect(body.classList.contains('pdf-no-transport-notes')).toBe(true)
|
||||
})
|
||||
|
||||
it('FE-PDF-TNOTES-004: the remembered choice comes back, next to the flowing layout', async () => {
|
||||
localStorage.setItem('trek_pdf_transport_notes', '0')
|
||||
localStorage.setItem('trek_pdf_page_break_per_day', '0')
|
||||
await downloadTripPDF({ ...richArgs, reservations: [withNote] })
|
||||
|
||||
expect(getIframe()!.srcdoc).toContain('<body class="pdf-flow pdf-no-transport-notes">')
|
||||
expect(notesToggle()!.getAttribute('aria-checked')).toBe('false')
|
||||
localStorage.removeItem('trek_pdf_page_break_per_day')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -29,17 +29,25 @@ import { onlyMyPlan } from './pdfScope'
|
||||
*/
|
||||
const PAGE_BREAK_KEY = 'trek_pdf_page_break_per_day'
|
||||
|
||||
function pageBreakPerDay(): boolean {
|
||||
/**
|
||||
* The notes of a flight, train or rental (#1571): printed by default, like the
|
||||
* notes of a place, and a switch in the preview leaves them out for a reader who
|
||||
* wants the plan without the fine print.
|
||||
*/
|
||||
const TRANSPORT_NOTES_KEY = 'trek_pdf_transport_notes'
|
||||
|
||||
/** A preview switch that is on unless it was turned off; both default to on. */
|
||||
function pdfSwitchOn(key: string): boolean {
|
||||
try {
|
||||
return localStorage.getItem(PAGE_BREAK_KEY) !== '0'
|
||||
return localStorage.getItem(key) !== '0'
|
||||
} catch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function rememberPageBreakPerDay(on: boolean): void {
|
||||
function rememberPdfSwitch(key: string, on: boolean): void {
|
||||
try {
|
||||
localStorage.setItem(PAGE_BREAK_KEY, on ? '1' : '0')
|
||||
localStorage.setItem(key, on ? '1' : '0')
|
||||
} catch {
|
||||
// A blocked localStorage costs the preference, not the export.
|
||||
}
|
||||
@@ -231,7 +239,10 @@ export async function downloadTripPDF({ trip, days, places, assignments: allStor
|
||||
? onlyMyPlan(allStored, allReservations, onlyUserId)
|
||||
: { assignments: allStored, reservations: allReservations }
|
||||
const assignments = planAssignments(stored, showServiceStops)
|
||||
const breaksPerDay = pageBreakPerDay()
|
||||
const breaksPerDay = pdfSwitchOn(PAGE_BREAK_KEY)
|
||||
const showTransportNotes = pdfSwitchOn(TRANSPORT_NOTES_KEY)
|
||||
// Set while the days render: the switch only shows when there is a note to hide.
|
||||
let hasTransportNotes = false
|
||||
const loc = _locale || undefined
|
||||
const tr = _t || (k => k)
|
||||
// The store read is the fallback, not the source: a caller that forgets the
|
||||
@@ -516,6 +527,8 @@ export async function downloadTripPDF({ trip, days, places, assignments: allStor
|
||||
const endTime = phase === 'single' ? fmtTime(splitReservationDateTime(r.reservation_end_time).time) : ''
|
||||
const time = [startTime, endTime].filter(Boolean).join(' – ')
|
||||
const titleHtml = `${spanLabel ? escHtml(spanLabel) + ': ' : ''}${escHtml(r.title)}`
|
||||
const transportNote = typeof r.notes === 'string' ? r.notes.trim() : ''
|
||||
if (transportNote) hasTransportNotes = true
|
||||
return `
|
||||
<div class="note-card" style="border-left: 3px solid ${color};">
|
||||
<div class="note-line" style="background: ${color};"></div>
|
||||
@@ -525,6 +538,7 @@ export async function downloadTripPDF({ trip, days, places, assignments: allStor
|
||||
${subtitleLines.filter(Boolean).map(s => `<div class="note-time">${escHtml(s)}</div>`).join('')}
|
||||
${locationLine ? `<div class="note-time">${escHtml(locationLine)}</div>` : ''}
|
||||
${r.confirmation_number ? `<div class="note-time" style="font-size:9px;">Code: ${escHtml(r.confirmation_number)}</div>` : ''}
|
||||
${transportNote ? `<div class="note-time transport-note">${escHtml(transportNote)}</div>` : ''}
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
@@ -751,6 +765,8 @@ export async function downloadTripPDF({ trip, days, places, assignments: allStor
|
||||
Flowing days butt against each other without the page edge between them. */
|
||||
.day-break { page-break-before: always; }
|
||||
.pdf-flow .day-break { page-break-before: auto; }
|
||||
.transport-note { font-style: italic; white-space: pre-line; }
|
||||
.pdf-no-transport-notes .transport-note { display: none; }
|
||||
.pdf-flow .day-section + .day-section { margin-top: 18px; }
|
||||
/* Hold a flowing day together. Without this the header bar can be placed at the
|
||||
foot of a sheet while its content moves to the next one, which then repeats
|
||||
@@ -868,7 +884,7 @@ export async function downloadTripPDF({ trip, days, places, assignments: allStor
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body${breaksPerDay ? '' : ' class="pdf-flow"'}>
|
||||
<body${bodyClassAttr(breaksPerDay, showTransportNotes)}>
|
||||
|
||||
<!-- Footer on every page -->
|
||||
<div class="pdf-footer">
|
||||
@@ -937,10 +953,8 @@ ${pluginSectionsHtml}
|
||||
header.innerHTML = `
|
||||
<span style="font-size:13px;font-weight:600;color:var(--text-primary);min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escHtml(trip?.title || tr('pdf.travelPlan'))}</span>
|
||||
<div style="display:flex;align-items:center;gap:8px">
|
||||
<label for="pdf-daybreak-toggle" style="font-size:12px;color:var(--text-muted);cursor:pointer;user-select:none">${escHtml(tr('pdf.pageBreakPerDay'))}</label>
|
||||
<button id="pdf-daybreak-toggle" type="button" role="switch" aria-checked="${breaksPerDay}" aria-label="${escHtml(tr('pdf.pageBreakPerDay'))}" style="${TOGGLE_TRACK} background:${trackColour(breaksPerDay)}">
|
||||
<span style="${TOGGLE_KNOB} left:${knobOffset(breaksPerDay)}"></span>
|
||||
</button>
|
||||
${hasTransportNotes ? previewSwitch('pdf-transport-notes-toggle', tr('pdf.transportNotes'), showTransportNotes) : ''}
|
||||
${previewSwitch('pdf-daybreak-toggle', tr('pdf.pageBreakPerDay'), breaksPerDay)}
|
||||
<button type="button" id="pdf-print-btn" style="display:flex;align-items:center;gap:5px;font-size:12px;font-weight:500;color:var(--text-muted);background:none;border:none;cursor:pointer;padding:4px 8px;border-radius:6px;font-family:inherit">${tr('pdf.saveAsPdf')}</button>
|
||||
<button type="button" id="pdf-close-btn" style="background:none;border:none;cursor:pointer;color:var(--text-faint);display:flex;padding:4px;border-radius:6px">
|
||||
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
||||
@@ -965,16 +979,40 @@ ${pluginSectionsHtml}
|
||||
const printBtn = header.querySelector<HTMLElement>('#pdf-print-btn')
|
||||
if (printBtn) printBtn.onclick = () => { iframe.contentWindow?.print() }
|
||||
|
||||
// The two layouts differ by one class, so switching is instant in the preview
|
||||
// and there is no need to re-fetch photos or rebuild the document.
|
||||
const dayBreakSwitch = header.querySelector<HTMLButtonElement>('#pdf-daybreak-toggle')
|
||||
if (dayBreakSwitch) dayBreakSwitch.onclick = () => {
|
||||
const on = dayBreakSwitch.getAttribute('aria-checked') !== 'true'
|
||||
dayBreakSwitch.setAttribute('aria-checked', String(on))
|
||||
dayBreakSwitch.style.background = trackColour(on)
|
||||
const knob = dayBreakSwitch.firstElementChild as HTMLElement | null
|
||||
if (knob) knob.style.left = knobOffset(on)
|
||||
rememberPageBreakPerDay(on)
|
||||
// Both choices differ by one class on <body>, so switching is instant in the
|
||||
// preview and there is no need to re-fetch photos or rebuild the document.
|
||||
wirePreviewSwitch(header, 'pdf-daybreak-toggle', on => {
|
||||
rememberPdfSwitch(PAGE_BREAK_KEY, on)
|
||||
iframe.contentDocument?.body.classList.toggle('pdf-flow', !on)
|
||||
})
|
||||
wirePreviewSwitch(header, 'pdf-transport-notes-toggle', on => {
|
||||
rememberPdfSwitch(TRANSPORT_NOTES_KEY, on)
|
||||
iframe.contentDocument?.body.classList.toggle('pdf-no-transport-notes', !on)
|
||||
})
|
||||
}
|
||||
|
||||
function bodyClassAttr(breaksPerDay: boolean, showTransportNotes: boolean): string {
|
||||
const classes = [!breaksPerDay && 'pdf-flow', !showTransportNotes && 'pdf-no-transport-notes'].filter(Boolean)
|
||||
return classes.length ? ` class="${classes.join(' ')}"` : ''
|
||||
}
|
||||
|
||||
/** A labelled switch for the preview header, as markup; wirePreviewSwitch gives it behaviour. */
|
||||
function previewSwitch(id: string, label: string, on: boolean): string {
|
||||
return `<label for="${id}" style="font-size:12px;color:var(--text-muted);cursor:pointer;user-select:none">${escHtml(label)}</label>
|
||||
<button id="${id}" type="button" role="switch" aria-checked="${on}" aria-label="${escHtml(label)}" style="${TOGGLE_TRACK} background:${trackColour(on)}">
|
||||
<span style="${TOGGLE_KNOB} left:${knobOffset(on)}"></span>
|
||||
</button>`
|
||||
}
|
||||
|
||||
function wirePreviewSwitch(header: HTMLElement, id: string, onChange: (on: boolean) => void): void {
|
||||
const button = header.querySelector<HTMLButtonElement>(`#${id}`)
|
||||
if (!button) return
|
||||
button.onclick = () => {
|
||||
const on = button.getAttribute('aria-checked') !== 'true'
|
||||
button.setAttribute('aria-checked', String(on))
|
||||
button.style.background = trackColour(on)
|
||||
const knob = button.firstElementChild as HTMLElement | null
|
||||
if (knob) knob.style.left = knobOffset(on)
|
||||
onChange(on)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'معاينة PDF',
|
||||
'pdf.saveAsPdf': 'حفظ كـ PDF',
|
||||
'pdf.pageBreakPerDay': 'فاصل صفحة لكل يوم',
|
||||
'pdf.transportNotes': 'ملاحظات التنقل',
|
||||
'pdf.mapTitle': 'نظرة عامة على المسار',
|
||||
'pdf.distanceLabel': 'المسافة',
|
||||
'pdf.mapCredit': 'حدود الدول: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'PDF önizləməsi',
|
||||
'pdf.saveAsPdf': 'PDF kimi yadda saxla',
|
||||
'pdf.pageBreakPerDay': 'Hər gün üçün yeni səhifə',
|
||||
'pdf.transportNotes': 'Nəqliyyat qeydləri',
|
||||
'pdf.mapTitle': 'Marşrutun icmalı',
|
||||
'pdf.distanceLabel': 'Məsafə',
|
||||
'pdf.mapCredit': 'Ölkə sərhədləri: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'Pré-visualização do PDF',
|
||||
'pdf.saveAsPdf': 'Salvar como PDF',
|
||||
'pdf.pageBreakPerDay': 'Quebra de página por dia',
|
||||
'pdf.transportNotes': 'Notas de transporte',
|
||||
'pdf.mapTitle': 'Visão geral da rota',
|
||||
'pdf.distanceLabel': 'Distância',
|
||||
'pdf.mapCredit': 'Contornos dos países: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'Vista prèvia del PDF',
|
||||
'pdf.saveAsPdf': 'Desa com a PDF',
|
||||
'pdf.pageBreakPerDay': 'Salt de pàgina per dia',
|
||||
'pdf.transportNotes': 'Notes del transport',
|
||||
'pdf.mapTitle': 'Resum de la ruta',
|
||||
'pdf.distanceLabel': 'Distància',
|
||||
'pdf.mapCredit': 'Contorns dels països: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'Náhled PDF',
|
||||
'pdf.saveAsPdf': 'Uložit jako PDF',
|
||||
'pdf.pageBreakPerDay': 'Zalomení stránky pro každý den',
|
||||
'pdf.transportNotes': 'Poznámky k dopravě',
|
||||
'pdf.mapTitle': 'Přehled trasy',
|
||||
'pdf.distanceLabel': 'Vzdálenost',
|
||||
'pdf.mapCredit': 'Obrysy zemí: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'PDF Vorschau',
|
||||
'pdf.saveAsPdf': 'Als PDF speichern',
|
||||
'pdf.pageBreakPerDay': 'Seitenumbruch pro Tag',
|
||||
'pdf.transportNotes': 'Notizen zu Transporten',
|
||||
'pdf.mapTitle': 'Routenübersicht',
|
||||
'pdf.distanceLabel': 'Strecke',
|
||||
'pdf.mapCredit': 'Ländergrenzen: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'PDF Preview',
|
||||
'pdf.saveAsPdf': 'Save as PDF',
|
||||
'pdf.pageBreakPerDay': 'Page break per day',
|
||||
'pdf.transportNotes': 'Transport notes',
|
||||
'pdf.mapTitle': 'Route overview',
|
||||
'pdf.distanceLabel': 'Distance',
|
||||
'pdf.mapCredit': 'Country outlines: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'Vista previa PDF',
|
||||
'pdf.saveAsPdf': 'Guardar como PDF',
|
||||
'pdf.pageBreakPerDay': 'Salto de página por día',
|
||||
'pdf.transportNotes': 'Notas de transporte',
|
||||
'pdf.mapTitle': 'Resumen de la ruta',
|
||||
'pdf.distanceLabel': 'Distancia',
|
||||
'pdf.mapCredit': 'Contornos de países: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'PDF-i eelvaade',
|
||||
'pdf.saveAsPdf': 'Salvesta PDF-ina',
|
||||
'pdf.pageBreakPerDay': 'Iga päev uuel lehel',
|
||||
'pdf.transportNotes': 'Transpordi märkmed',
|
||||
'pdf.mapTitle': 'Marsruudi ülevaade',
|
||||
'pdf.distanceLabel': 'Vahemaa',
|
||||
'pdf.mapCredit': 'Riigipiirid: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'Aperçu PDF',
|
||||
'pdf.saveAsPdf': 'Enregistrer en PDF',
|
||||
'pdf.pageBreakPerDay': 'Saut de page par jour',
|
||||
'pdf.transportNotes': 'Notes de transport',
|
||||
'pdf.mapTitle': 'Aperçu de l\'itinéraire',
|
||||
'pdf.distanceLabel': 'Distance',
|
||||
'pdf.mapCredit': 'Contours des pays : geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'Προεπισκόπηση PDF',
|
||||
'pdf.saveAsPdf': 'Αποθήκευση ως PDF',
|
||||
'pdf.pageBreakPerDay': 'Αλλαγή σελίδας ανά ημέρα',
|
||||
'pdf.transportNotes': 'Σημειώσεις μεταφορών',
|
||||
'pdf.mapTitle': 'Επισκόπηση διαδρομής',
|
||||
'pdf.distanceLabel': 'Απόσταση',
|
||||
'pdf.mapCredit': 'Περιγράμματα χωρών: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'PDF előnézet',
|
||||
'pdf.saveAsPdf': 'Mentés PDF-ként',
|
||||
'pdf.pageBreakPerDay': 'Oldaltörés naponta',
|
||||
'pdf.transportNotes': 'Közlekedési jegyzetek',
|
||||
'pdf.mapTitle': 'Útvonal áttekintése',
|
||||
'pdf.distanceLabel': 'Távolság',
|
||||
'pdf.mapCredit': 'Országhatárok: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'Pratinjau PDF',
|
||||
'pdf.saveAsPdf': 'Simpan sebagai PDF',
|
||||
'pdf.pageBreakPerDay': 'Pemisah halaman per hari',
|
||||
'pdf.transportNotes': 'Catatan transportasi',
|
||||
'pdf.mapTitle': 'Ringkasan rute',
|
||||
'pdf.distanceLabel': 'Jarak',
|
||||
'pdf.mapCredit': 'Garis batas negara: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'Anteprima PDF',
|
||||
'pdf.saveAsPdf': 'Salva come PDF',
|
||||
'pdf.pageBreakPerDay': 'Interruzione di pagina per giorno',
|
||||
'pdf.transportNotes': 'Note sui trasporti',
|
||||
'pdf.mapTitle': 'Panoramica del percorso',
|
||||
'pdf.distanceLabel': 'Distanza',
|
||||
'pdf.mapCredit': 'Confini dei paesi: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'PDFプレビュー',
|
||||
'pdf.saveAsPdf': 'PDFとして保存',
|
||||
'pdf.pageBreakPerDay': '日ごとに改ページ',
|
||||
'pdf.transportNotes': '交通機関のメモ',
|
||||
'pdf.mapTitle': 'ルート概要',
|
||||
'pdf.distanceLabel': '距離',
|
||||
'pdf.mapCredit': '国境線: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'PDF 미리보기',
|
||||
'pdf.saveAsPdf': 'PDF로 저장',
|
||||
'pdf.pageBreakPerDay': '날짜별 페이지 나누기',
|
||||
'pdf.transportNotes': '교통편 메모',
|
||||
'pdf.mapTitle': '경로 개요',
|
||||
'pdf.distanceLabel': '거리',
|
||||
'pdf.mapCredit': '국가 윤곽: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'PDF-voorbeeld',
|
||||
'pdf.saveAsPdf': 'Opslaan als PDF',
|
||||
'pdf.pageBreakPerDay': 'Pagina-einde per dag',
|
||||
'pdf.transportNotes': 'Notities bij vervoer',
|
||||
'pdf.mapTitle': 'Routeoverzicht',
|
||||
'pdf.distanceLabel': 'Afstand',
|
||||
'pdf.mapCredit': 'Landcontouren: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'Podgląd PDF',
|
||||
'pdf.saveAsPdf': 'Zapisz jako PDF',
|
||||
'pdf.pageBreakPerDay': 'Podział strony dla każdego dnia',
|
||||
'pdf.transportNotes': 'Notatki do transportu',
|
||||
'pdf.mapTitle': 'Przegląd trasy',
|
||||
'pdf.distanceLabel': 'Dystans',
|
||||
'pdf.mapCredit': 'Kontury krajów: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'Предпросмотр PDF',
|
||||
'pdf.saveAsPdf': 'Сохранить как PDF',
|
||||
'pdf.pageBreakPerDay': 'Разрыв страницы для каждого дня',
|
||||
'pdf.transportNotes': 'Заметки к транспорту',
|
||||
'pdf.mapTitle': 'Обзор маршрута',
|
||||
'pdf.distanceLabel': 'Расстояние',
|
||||
'pdf.mapCredit': 'Контуры стран: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'Náhľad PDF',
|
||||
'pdf.saveAsPdf': 'Uložiť ako PDF',
|
||||
'pdf.pageBreakPerDay': 'Zalomenie strany pre každý deň',
|
||||
'pdf.transportNotes': 'Poznámky k doprave',
|
||||
'pdf.mapTitle': 'Prehľad trasy',
|
||||
'pdf.distanceLabel': 'Vzdialenosť',
|
||||
'pdf.mapCredit': 'Obrysy krajín: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'Förhandsgranskning av PDF',
|
||||
'pdf.saveAsPdf': 'Spara som PDF',
|
||||
'pdf.pageBreakPerDay': 'Sidbrytning per dag',
|
||||
'pdf.transportNotes': 'Anteckningar om transporter',
|
||||
'pdf.mapTitle': 'Ruttöversikt',
|
||||
'pdf.distanceLabel': 'Sträcka',
|
||||
'pdf.mapCredit': 'Landskonturer: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': "ดูตัวอย่าง PDF",
|
||||
'pdf.saveAsPdf': "บันทึกเป็น PDF",
|
||||
'pdf.pageBreakPerDay': "ขึ้นหน้าใหม่ทุกวัน",
|
||||
'pdf.transportNotes': 'บันทึกการเดินทาง',
|
||||
'pdf.mapTitle': "ภาพรวมเส้นทาง",
|
||||
'pdf.distanceLabel': "ระยะทาง",
|
||||
'pdf.mapCredit': "ขอบเขตประเทศ: geoBoundaries (CC BY 4.0)",
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'PDF Önizleme',
|
||||
'pdf.saveAsPdf': 'PDF olarak Kaydet',
|
||||
'pdf.pageBreakPerDay': 'Her gün için sayfa sonu',
|
||||
'pdf.transportNotes': 'Ulaşım notları',
|
||||
'pdf.mapTitle': 'Rota genel görünümü',
|
||||
'pdf.distanceLabel': 'Mesafe',
|
||||
'pdf.mapCredit': 'Ülke sınırları: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'Попередній перегляд PDF',
|
||||
'pdf.saveAsPdf': 'Зберегти як PDF',
|
||||
'pdf.pageBreakPerDay': 'Розрив сторінки для кожного дня',
|
||||
'pdf.transportNotes': 'Нотатки до транспорту',
|
||||
'pdf.mapTitle': 'Огляд маршруту',
|
||||
'pdf.distanceLabel': 'Відстань',
|
||||
'pdf.mapCredit': 'Контури країн: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'PDF Xem trước',
|
||||
'pdf.saveAsPdf': 'Lưu dưới dạng PDF',
|
||||
'pdf.pageBreakPerDay': 'Ngắt trang cho mỗi ngày',
|
||||
'pdf.transportNotes': 'Ghi chú phương tiện',
|
||||
'pdf.mapTitle': 'Tổng quan lộ trình',
|
||||
'pdf.distanceLabel': 'Quãng đường',
|
||||
'pdf.mapCredit': 'Đường viền quốc gia: geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'PDF 預覽',
|
||||
'pdf.saveAsPdf': '儲存為 PDF',
|
||||
'pdf.pageBreakPerDay': '每天分頁',
|
||||
'pdf.transportNotes': '交通備註',
|
||||
'pdf.mapTitle': '路線總覽',
|
||||
'pdf.distanceLabel': '距離',
|
||||
'pdf.mapCredit': '國家輪廓:geoBoundaries (CC BY 4.0)',
|
||||
|
||||
@@ -7,6 +7,7 @@ const pdf: TranslationStrings = {
|
||||
'pdf.preview': 'PDF 预览',
|
||||
'pdf.saveAsPdf': '保存为 PDF',
|
||||
'pdf.pageBreakPerDay': '每天分页',
|
||||
'pdf.transportNotes': '交通备注',
|
||||
'pdf.mapTitle': '路线总览',
|
||||
'pdf.distanceLabel': '距离',
|
||||
'pdf.mapCredit': '国家轮廓:geoBoundaries (CC BY 4.0)',
|
||||
|
||||
Reference in New Issue
Block a user