mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 13:21:46 +00:00
6ef3c7ae6b
* feat(reservations): native booking-confirmation import via KDE KItinerary
Adds a two-step preview → confirm flow for importing booking emails,
PDFs, PKPass and HTML confirmations. The server invokes the KDE
kitinerary-extractor binary, maps JSON-LD schema.org output to TREK
reservation shapes, and persists via the existing createReservation
pipeline (accommodations, budget, places, WebSocket broadcasts).
- NestJS BookingImportModule: preview + confirm endpoints under
/api/trips/:tripId/reservations/import/booking{,/confirm}
- KitineraryExtractorService: spawns the binary, filters stderr noise,
handles QDateTime (@value) timezone-aware datetimes
- kitinerary-mapper: FlightReservation, TrainReservation, BusReservation,
BoatReservation, LodgingReservation, FoodEstablishmentReservation,
RentalCarReservation, EventReservation → typed preview items
- BookingImportService: auto-creates place rows; geocodes venues without
coordinates via Nominatim (name+address → address → name fallback);
resolves day IDs for accommodation linking
- BookingImportModal: drag-and-drop multi-file upload, preview cards
with type icons, per-item exclude toggle, confirm step
- Shared Zod contracts: BookingImportPreviewItem, PreviewResponse,
ConfirmRequest, ConfirmResponse — consumed by controller, service,
API client and modal
- Dockerfile: node:24-trixie-slim runtime; amd64 downloads KDE static
binary + locales; arm64 installs libkitinerary-bin + symlinks to
fixed path; ENV KITINERARY_EXTRACTOR_PATH set for both arches
- /api/health/features exposes { bookingImport: boolean } so the UI
hides the Import button when the binary is absent
- i18n keys (English), wiki docs, API.md, README one-liner
* i18n: add booking import translations for all 19 non-English locales
Adds 17 reservations.import.* keys and undo.importBooking to ar, br, cs,
de, es, fr, gr, hu, id, it, ja, ko, nl, pl, ru, tr, uk, zh, zh-TW.
* chore: enforce i18n parity
* docs(wiki): add KItinerary local setup instructions to dev environment guide
142 lines
9.0 KiB
TypeScript
142 lines
9.0 KiB
TypeScript
import type { TranslationStrings } from '../types';
|
||
|
||
const reservations: TranslationStrings = {
|
||
'reservations.title': 'Бронювання',
|
||
'reservations.empty': 'Поки немає бронювань',
|
||
'reservations.emptyHint': 'Додайте бронювання на авіаквитки, готелі та інше',
|
||
'reservations.add': 'Додати бронювання',
|
||
'reservations.addManual': 'Ручне бронювання',
|
||
'reservations.placeHint':
|
||
'Порада: бронювання краще створювати безпосередньо з місця, щоб пов’язати їх з планом дня.',
|
||
'reservations.confirmed': 'Підтверджено',
|
||
'reservations.pending': 'Очікування',
|
||
'reservations.summary': '{confirmed} підтвр., {pending} очікувань',
|
||
'reservations.fromPlan': 'З плану',
|
||
'reservations.showFiles': 'Показати файли',
|
||
'reservations.editTitle': 'Редагувати бронювання',
|
||
'reservations.status': 'Статус',
|
||
'reservations.datetime': 'Дата і час',
|
||
'reservations.startTime': 'Час початку',
|
||
'reservations.endTime': 'Час закінчення',
|
||
'reservations.date': 'Дата',
|
||
'reservations.time': 'Час',
|
||
'reservations.timeAlt': 'Час (альтернативний, напр. 19:30)',
|
||
'reservations.notes': 'Нотатки',
|
||
'reservations.notesPlaceholder': 'Додаткові нотатки...',
|
||
'reservations.meta.airline': 'Авіакомпанія',
|
||
'reservations.meta.flightNumber': 'Номер рейсу',
|
||
'reservations.meta.from': 'Звідки',
|
||
'reservations.meta.to': 'Куди',
|
||
'reservations.needsReview': 'Перевірити',
|
||
'reservations.needsReviewHint':
|
||
'Аеропорт не вдалося визначити автоматично — підтвердіть місцезнаходження.',
|
||
'reservations.searchLocation': 'Шукати станцію, порт, адресу...',
|
||
'reservations.meta.trainNumber': 'Номер поїзда',
|
||
'reservations.meta.platform': 'Платформа',
|
||
'reservations.meta.seat': 'Місце',
|
||
'reservations.meta.checkIn': 'Заїзд',
|
||
'reservations.meta.checkInUntil': 'Заселення до',
|
||
'reservations.meta.checkOut': 'Виїзд',
|
||
'reservations.meta.linkAccommodation': 'Житло',
|
||
'reservations.meta.pickAccommodation': 'Прив’язати до житла',
|
||
'reservations.meta.noAccommodation': 'Ні',
|
||
'reservations.meta.hotelPlace': 'Житло',
|
||
'reservations.meta.pickHotel': 'Оберіть житло',
|
||
'reservations.meta.fromDay': 'З',
|
||
'reservations.meta.toDay': 'По',
|
||
'reservations.meta.selectDay': 'Оберіть день',
|
||
'reservations.type.flight': 'Авіаквиток',
|
||
'reservations.type.hotel': 'Житло',
|
||
'reservations.type.restaurant': 'Ресторан',
|
||
'reservations.type.train': 'Поїзд',
|
||
'reservations.type.car': 'Автомобіль',
|
||
'reservations.type.cruise': 'Круїз',
|
||
'reservations.type.event': 'Заходи',
|
||
'reservations.type.tour': 'Екскурсія',
|
||
'reservations.type.other': 'Інше',
|
||
'reservations.type.bus': 'Автобус',
|
||
'reservations.type.ferry': 'Пором',
|
||
'reservations.type.bicycle': 'Велосипед',
|
||
'reservations.type.taxi': 'Таксі',
|
||
'reservations.type.transport_other': 'Інше',
|
||
'reservations.confirm.delete':
|
||
'Ви впевнені, що хочете видалити бронювання «{name}»?',
|
||
'reservations.confirm.deleteTitle': 'Видалити бронювання?',
|
||
'reservations.confirm.deleteBody': '«{name}» буде видалено назавжди.',
|
||
'reservations.toast.updated': 'Бронювання оновлено',
|
||
'reservations.toast.removed': 'Бронювання видалено',
|
||
'reservations.toast.fileUploaded': 'Файл завантажено',
|
||
'reservations.toast.uploadError': 'Помилка завантаження',
|
||
'reservations.newTitle': 'Нове бронювання',
|
||
'reservations.bookingType': 'Тип бронювання',
|
||
'reservations.titleLabel': 'Назва',
|
||
'reservations.titlePlaceholder':
|
||
'наприклад, Lufthansa LH123, Hotel Adlon, ...',
|
||
'reservations.locationAddress': 'Місцезнаходження / Адреса',
|
||
'reservations.locationPlaceholder': 'Адреса, аеропорт, готель...',
|
||
'reservations.confirmationCode': 'Код бронювання',
|
||
'reservations.confirmationPlaceholder': 'наприклад, ABC12345',
|
||
'reservations.day': 'День',
|
||
'reservations.noDay': 'Без дня',
|
||
'reservations.place': 'Місце',
|
||
'reservations.noPlace': 'Без місця',
|
||
'reservations.pendingSave': 'буде збережено…',
|
||
'reservations.uploading': 'Завантаження...',
|
||
'reservations.attachFile': 'Прикріпити файл',
|
||
'reservations.linkExisting': 'Прив’язати існуючий файл',
|
||
'reservations.toast.saveError': 'Помилка збереження',
|
||
'reservations.toast.updateError': 'Помилка оновлення',
|
||
'reservations.toast.deleteError': 'Помилка видалення',
|
||
'reservations.confirm.remove': 'Видалити бронювання для «{name}»?',
|
||
'reservations.linkAssignment': 'Прив’язати до призначення дня',
|
||
'reservations.pickAssignment': 'Оберіть призначення з вашого плану...',
|
||
'reservations.noAssignment': 'Без прив’язки (самостійно)',
|
||
'reservations.price': 'Ціна',
|
||
'reservations.budgetCategory': 'Категорія бюджету',
|
||
'reservations.budgetCategoryPlaceholder': 'наприклад, Транспорт, Проживання',
|
||
'reservations.budgetCategoryAuto': 'Авто (за типом бронювання)',
|
||
'reservations.budgetHint':
|
||
'При збереженні буде автоматично створено запис бюджету.',
|
||
'reservations.departureDate': 'Виліт',
|
||
'reservations.arrivalDate': 'Приліт',
|
||
'reservations.departureTime': 'Час вильоту',
|
||
'reservations.arrivalTime': 'Час прильоту',
|
||
'reservations.pickupDate': 'Отримання',
|
||
'reservations.returnDate': 'Повернення',
|
||
'reservations.pickupTime': 'Час отримання',
|
||
'reservations.returnTime': 'Час повернення',
|
||
'reservations.endDate': 'Дата закінчення',
|
||
'reservations.meta.departureTimezone': 'TZ вильоту',
|
||
'reservations.meta.arrivalTimezone': 'TZ прильоту',
|
||
'reservations.span.departure': 'Виліт',
|
||
'reservations.span.arrival': 'Приліт',
|
||
'reservations.span.inTransit': 'У дорозі',
|
||
'reservations.span.pickup': 'Отримання',
|
||
'reservations.span.return': 'Повернення',
|
||
'reservations.span.active': 'Активно',
|
||
'reservations.span.start': 'Початок',
|
||
'reservations.span.end': 'Кінець',
|
||
'reservations.span.ongoing': 'Триває',
|
||
'reservations.validation.endBeforeStart':
|
||
'Дата/час закінчення повинен бути пізніше дати/часу початку',
|
||
'reservations.addBooking': 'Добавить бронирование',
|
||
'reservations.import.title': 'Імпорт підтверджень бронювання',
|
||
'reservations.import.cta': 'Імпортувати з файлу',
|
||
'reservations.import.dropHere': 'Перетягніть файли підтверджень бронювання сюди або натисніть для вибору',
|
||
'reservations.import.dropActive': 'Відпустіть файли для імпорту',
|
||
'reservations.import.acceptedFormats': 'Підтримуються: EML, PDF, PKPass, HTML, TXT (макс. 10 МБ кожен, до 5 файлів)',
|
||
'reservations.import.parsing': 'Обробка файлів…',
|
||
'reservations.import.previewHeading': 'Знайдено {count} бронювання(нь)',
|
||
'reservations.import.previewEmpty': 'З завантажених файлів не вдалося витягти бронювання.',
|
||
'reservations.import.removeItem': 'Видалити',
|
||
'reservations.import.confirm': 'Імпортувати {count} бронювання(нь)',
|
||
'reservations.import.back': 'Назад',
|
||
'reservations.import.success': '{count} бронювання(нь) імпортовано',
|
||
'reservations.import.partialFailure': '{created} імпортовано, {failed} не вдалося',
|
||
'reservations.import.error': 'Обробка не вдалася. Переконайтесь, що файл є дійсним підтвердженням бронювання.',
|
||
'reservations.import.unavailable': 'Імпорт бронювань недоступний на цьому сервері.',
|
||
'reservations.import.unsupportedFormat': 'Непідтримуваний формат файлу. Використовуйте EML, PDF, PKPass, HTML або TXT.',
|
||
'reservations.import.fileTooLarge': 'Файл «{name}» перевищує обмеження в 10 МБ.',
|
||
};
|
||
export default reservations;
|