From 3dc9ac6ac6f161dfa4376786e8a329b9d003984d Mon Sep 17 00:00:00 2001 From: fbnlrz <137117973+fbnlrz@users.noreply.github.com> Date: Tue, 21 Jul 2026 22:53:11 +0200 Subject: [PATCH] fix(airtrail): stop labelling a merged multi-leg import "removed in AirTrail" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A multi-leg flight imported "as one flight with a layover" is detached from sync by design — AirTrail has no single flight to round-trip a layover chain to, so it is created with sync_enabled=0 (#1535). The reservations badge collapsed every sync_enabled=0 AirTrail row into "Not synced" with the tooltip "This flight was removed in AirTrail", which is false for a merged booking that was never removed (#1646). Give the multi-leg case (metadata.legs.length > 1) its own state: show the normal AirTrail badge with a truthful tooltip explaining it stays a one-time import. A genuinely removed single-leg flight still shows "Not synced". Adds reservations.airtrail.layoverHint across all locales. --- .../Planner/ReservationsPanel.test.tsx | 29 +++++++++++++++++++ .../components/Planner/ReservationsPanel.tsx | 16 ++++++++-- shared/src/i18n/ar/reservations.ts | 1 + shared/src/i18n/br/reservations.ts | 1 + shared/src/i18n/ca/reservations.ts | 1 + shared/src/i18n/cs/reservations.ts | 1 + shared/src/i18n/de/reservations.ts | 1 + shared/src/i18n/en/reservations.ts | 1 + shared/src/i18n/es/reservations.ts | 1 + shared/src/i18n/fr/reservations.ts | 1 + shared/src/i18n/gr/reservations.ts | 1 + shared/src/i18n/hu/reservations.ts | 1 + shared/src/i18n/id/reservations.ts | 1 + shared/src/i18n/it/reservations.ts | 1 + shared/src/i18n/ja/reservations.ts | 1 + shared/src/i18n/ko/reservations.ts | 1 + shared/src/i18n/nl/reservations.ts | 1 + shared/src/i18n/pl/reservations.ts | 1 + shared/src/i18n/ru/reservations.ts | 1 + shared/src/i18n/sv/reservations.ts | 1 + shared/src/i18n/tr/reservations.ts | 1 + shared/src/i18n/uk/reservations.ts | 1 + shared/src/i18n/vi/reservations.ts | 1 + shared/src/i18n/zh-TW/reservations.ts | 1 + shared/src/i18n/zh/reservations.ts | 1 + 25 files changed, 65 insertions(+), 3 deletions(-) diff --git a/client/src/components/Planner/ReservationsPanel.test.tsx b/client/src/components/Planner/ReservationsPanel.test.tsx index 0a8995ec6..ccd3a797f 100644 --- a/client/src/components/Planner/ReservationsPanel.test.tsx +++ b/client/src/components/Planner/ReservationsPanel.test.tsx @@ -464,4 +464,33 @@ describe('ReservationsPanel', () => { const text = document.body.textContent || ''; expect(text.indexOf('Mid flight')).toBeLessThan(text.indexOf('Hotel stay')); }); + + // AirTrail sync badge — three states (#1646) + it('FE-PLANNER-RESP-046: a synced AirTrail flight shows the AirTrail badge', () => { + const res = buildReservation({ title: 'Synced flight', type: 'flight', external_source: 'airtrail', sync_enabled: 1 } as any); + render(); + expect(screen.getByTitle('Synced from AirTrail — edits stay in sync both ways.')).toBeInTheDocument(); + expect(screen.queryByText('Not synced')).not.toBeInTheDocument(); + }); + + it('FE-PLANNER-RESP-047: a multi-leg import shows the layover hint, not the "removed" message', () => { + const res = buildReservation({ + title: 'Layover flight', type: 'flight', external_source: 'airtrail', sync_enabled: 0, + metadata: JSON.stringify({ legs: [{ from: 'AMS' }, { from: 'IST' }] }), + } as any); + render(); + // Not falsely labelled "Not synced" / "removed in AirTrail"… + expect(screen.queryByText('Not synced')).not.toBeInTheDocument(); + // …and carries the truthful layover explanation. + expect( + screen.getByTitle('Imported from AirTrail. A multi-leg flight with a layover has no single AirTrail flight to sync back to, so it stays as a one-time import.'), + ).toBeInTheDocument(); + }); + + it('FE-PLANNER-RESP-048: a single-leg flight removed upstream still shows "Not synced"', () => { + const res = buildReservation({ title: 'Removed flight', type: 'flight', external_source: 'airtrail', sync_enabled: 0 } as any); + render(); + expect(screen.getByText('Not synced')).toBeInTheDocument(); + expect(screen.getByTitle('This flight was removed in AirTrail and no longer syncs.')).toBeInTheDocument(); + }); }); diff --git a/client/src/components/Planner/ReservationsPanel.tsx b/client/src/components/Planner/ReservationsPanel.tsx index 302b0a108..3b6d314d4 100644 --- a/client/src/components/Planner/ReservationsPanel.tsx +++ b/client/src/components/Planner/ReservationsPanel.tsx @@ -97,6 +97,16 @@ function ReservationCard({ r, tripId, onEdit, onDelete, files = [], onNavigateTo const typeInfo = getType(r.type) const TypeIcon = typeInfo.Icon const confirmed = r.status === 'confirmed' + // A multi-leg AirTrail import is detached from sync *by design* — AirTrail has + // no single flight to round-trip a layover chain to, so it's created with + // sync_enabled=0 (#1535). Distinguish it from a flight that was removed + // upstream so the badge doesn't falsely claim it "was removed in AirTrail" (#1646). + const airtrailMultiLeg = r.external_source === 'airtrail' && !r.sync_enabled && (() => { + try { + const m = typeof r.metadata === 'string' ? JSON.parse(r.metadata || '{}') : (r.metadata || {}) + return Array.isArray(m?.legs) && m.legs.length > 1 + } catch { return false } + })() const attachedFiles = files.filter(f => f.reservation_id === r.id || (f.linked_reservation_ids || []).includes(r.id)) const linked = r.assignment_id ? assignmentLookup[r.assignment_id] : null const [showDeleteConfirm, setShowDeleteConfirm] = useState(false) @@ -196,12 +206,12 @@ function ReservationCard({ r, tripId, onEdit, onDelete, files = [], onNavigateTo ) : null} {r.external_source === 'airtrail' ? ( - {r.sync_enabled ? t('reservations.airtrail.synced') : t('reservations.airtrail.notSynced')} + {r.sync_enabled || airtrailMultiLeg ? t('reservations.airtrail.synced') : t('reservations.airtrail.notSynced')} ) : null} diff --git a/shared/src/i18n/ar/reservations.ts b/shared/src/i18n/ar/reservations.ts index b4090192a..1aa8dbd15 100644 --- a/shared/src/i18n/ar/reservations.ts +++ b/shared/src/i18n/ar/reservations.ts @@ -150,6 +150,7 @@ const reservations: TranslationStrings = { 'reservations.airtrail.syncedHint': 'متزامن من AirTrail — تبقى التعديلات متزامنة في الاتجاهين.', 'reservations.airtrail.notSynced': 'غير متزامن', 'reservations.airtrail.notSyncedHint': 'تمت إزالة هذه الرحلة في AirTrail ولم تعد متزامنة.', + 'reservations.airtrail.layoverHint': 'تم الاستيراد من AirTrail. الرحلة متعددة المراحل مع توقف لا تقابلها رحلة واحدة في AirTrail للمزامنة معها، لذا تبقى استيرادًا لمرة واحدة.', 'reservations.airtrail.loadError': 'تعذّر تحميل رحلاتك من AirTrail.', 'reservations.airtrail.imported': 'تم استيراد {count} رحلة/رحلات', 'reservations.airtrail.skippedDuplicate': '{count} موجودة بالفعل في هذه الرحلة، تم تخطّيها', diff --git a/shared/src/i18n/br/reservations.ts b/shared/src/i18n/br/reservations.ts index cbed3dfb3..6298deaf3 100644 --- a/shared/src/i18n/br/reservations.ts +++ b/shared/src/i18n/br/reservations.ts @@ -150,6 +150,7 @@ const reservations: TranslationStrings = { 'Sincronizado do AirTrail — as edições permanecem em sincronia nos dois sentidos.', 'reservations.airtrail.notSynced': 'Não sincronizado', 'reservations.airtrail.notSyncedHint': 'Este voo foi removido no AirTrail e não sincroniza mais.', + 'reservations.airtrail.layoverHint': 'Importado do AirTrail. Um voo de vários trechos com conexão não tem um único voo do AirTrail para sincronizar, então permanece como uma importação única.', 'reservations.airtrail.loadError': 'Não foi possível carregar seus voos do AirTrail.', 'reservations.airtrail.imported': '{count} voo(s) importado(s)', 'reservations.airtrail.skippedDuplicate': '{count} já nesta viagem, ignorado(s)', diff --git a/shared/src/i18n/ca/reservations.ts b/shared/src/i18n/ca/reservations.ts index 071d3cce3..4cd2f5e27 100644 --- a/shared/src/i18n/ca/reservations.ts +++ b/shared/src/i18n/ca/reservations.ts @@ -155,6 +155,7 @@ const reservations: TranslationStrings = { "Sincronitzat des d'AirTrail — les edicions es mantenen sincronitzades en ambdós sentits.", 'reservations.airtrail.notSynced': 'No sincronitzat', 'reservations.airtrail.notSyncedHint': "Aquest vol s'ha eliminat a AirTrail i ja no es sincronitza.", + 'reservations.airtrail.layoverHint': 'Importat des d\'AirTrail. Un vol de diversos trams amb escala no té cap vol únic d\'AirTrail amb què sincronitzar-se, així que es manté com una importació única.', 'reservations.airtrail.loadError': "No s'han pogut carregar els teus vols d'AirTrail.", 'reservations.airtrail.imported': '{count} vol(s) importat(s)', 'reservations.airtrail.skippedDuplicate': '{count} ja existents en aquest viatge, ignorats', diff --git a/shared/src/i18n/cs/reservations.ts b/shared/src/i18n/cs/reservations.ts index c488cdf9c..fc5fc81d8 100644 --- a/shared/src/i18n/cs/reservations.ts +++ b/shared/src/i18n/cs/reservations.ts @@ -150,6 +150,7 @@ const reservations: TranslationStrings = { 'reservations.airtrail.syncedHint': 'Synchronizováno z AirTrail – úpravy zůstávají synchronní v obou směrech.', 'reservations.airtrail.notSynced': 'Nesynchronizováno', 'reservations.airtrail.notSyncedHint': 'Tento let byl v AirTrail odstraněn a již se nesynchronizuje.', + 'reservations.airtrail.layoverHint': 'Importováno z AirTrail. Let s více úseky a mezipřistáním nemá jediný let AirTrail, se kterým by se synchronizoval, takže zůstává jednorázovým importem.', 'reservations.airtrail.loadError': 'Vaše lety z AirTrail se nepodařilo načíst.', 'reservations.airtrail.imported': 'Importováno letů: {count}', 'reservations.airtrail.skippedDuplicate': 'Již v tomto výletu: {count}, přeskočeno', diff --git a/shared/src/i18n/de/reservations.ts b/shared/src/i18n/de/reservations.ts index c57ad758a..74dbe91f5 100644 --- a/shared/src/i18n/de/reservations.ts +++ b/shared/src/i18n/de/reservations.ts @@ -155,6 +155,7 @@ const reservations: TranslationStrings = { 'reservations.airtrail.syncedHint': 'Aus AirTrail synchronisiert — Änderungen bleiben in beide Richtungen synchron.', 'reservations.airtrail.notSynced': 'Nicht synchronisiert', 'reservations.airtrail.notSyncedHint': 'Dieser Flug wurde in AirTrail gelöscht und wird nicht mehr synchronisiert.', + 'reservations.airtrail.layoverHint': 'Aus AirTrail importiert. Ein mehrteiliger Flug mit Zwischenstopp lässt sich keinem einzelnen AirTrail-Flug zuordnen und bleibt daher ein einmaliger Import.', 'reservations.airtrail.loadError': 'Ihre AirTrail-Flüge konnten nicht geladen werden.', 'reservations.airtrail.imported': '{count} Flug/Flüge importiert', 'reservations.airtrail.skippedDuplicate': '{count} bereits in dieser Reise, übersprungen', diff --git a/shared/src/i18n/en/reservations.ts b/shared/src/i18n/en/reservations.ts index 34acf4e97..0b4703c2c 100644 --- a/shared/src/i18n/en/reservations.ts +++ b/shared/src/i18n/en/reservations.ts @@ -151,6 +151,7 @@ const reservations: TranslationStrings = { 'reservations.airtrail.syncedHint': 'Synced from AirTrail — edits stay in sync both ways.', 'reservations.airtrail.notSynced': 'Not synced', 'reservations.airtrail.notSyncedHint': 'This flight was removed in AirTrail and no longer syncs.', + 'reservations.airtrail.layoverHint': 'Imported from AirTrail. A multi-leg flight with a layover has no single AirTrail flight to sync back to, so it stays as a one-time import.', 'reservations.airtrail.loadError': 'Could not load your AirTrail flights.', 'reservations.airtrail.imported': '{count} flight(s) imported', 'reservations.airtrail.skippedDuplicate': '{count} already in this trip, skipped', diff --git a/shared/src/i18n/es/reservations.ts b/shared/src/i18n/es/reservations.ts index c10fdb988..6e8d306eb 100644 --- a/shared/src/i18n/es/reservations.ts +++ b/shared/src/i18n/es/reservations.ts @@ -153,6 +153,7 @@ const reservations: TranslationStrings = { 'Sincronizado desde AirTrail: las ediciones se mantienen sincronizadas en ambos sentidos.', 'reservations.airtrail.notSynced': 'No sincronizado', 'reservations.airtrail.notSyncedHint': 'Este vuelo se eliminó en AirTrail y ya no se sincroniza.', + 'reservations.airtrail.layoverHint': 'Importado desde AirTrail. Un vuelo de varios tramos con escala no tiene un único vuelo de AirTrail con el que sincronizarse, por lo que se mantiene como una importación única.', 'reservations.airtrail.loadError': 'No se pudieron cargar tus vuelos de AirTrail.', 'reservations.airtrail.imported': '{count} vuelo(s) importado(s)', 'reservations.airtrail.skippedDuplicate': '{count} ya en este viaje, omitido(s)', diff --git a/shared/src/i18n/fr/reservations.ts b/shared/src/i18n/fr/reservations.ts index 4d11ea14c..2b4b32b4b 100644 --- a/shared/src/i18n/fr/reservations.ts +++ b/shared/src/i18n/fr/reservations.ts @@ -156,6 +156,7 @@ const reservations: TranslationStrings = { 'Synchronisé depuis AirTrail — les modifications restent synchronisées dans les deux sens.', 'reservations.airtrail.notSynced': 'Non synchronisé', 'reservations.airtrail.notSyncedHint': "Ce vol a été supprimé dans AirTrail et n'est plus synchronisé.", + 'reservations.airtrail.layoverHint': 'Importé depuis AirTrail. Un vol à plusieurs segments avec escale ne correspond à aucun vol AirTrail unique à synchroniser ; il reste donc un import ponctuel.', 'reservations.airtrail.loadError': 'Impossible de charger vos vols AirTrail.', 'reservations.airtrail.imported': '{count} vol(s) importé(s)', 'reservations.airtrail.skippedDuplicate': '{count} déjà dans ce voyage, ignoré(s)', diff --git a/shared/src/i18n/gr/reservations.ts b/shared/src/i18n/gr/reservations.ts index fe91b966d..798f77671 100644 --- a/shared/src/i18n/gr/reservations.ts +++ b/shared/src/i18n/gr/reservations.ts @@ -154,6 +154,7 @@ const reservations: TranslationStrings = { 'Συγχρονισμένο από το AirTrail — οι αλλαγές συγχρονίζονται και προς τις δύο κατευθύνσεις.', 'reservations.airtrail.notSynced': 'Μη συγχρονισμένο', 'reservations.airtrail.notSyncedHint': 'Αυτή η πτήση αφαιρέθηκε στο AirTrail και δεν συγχρονίζεται πλέον.', + 'reservations.airtrail.layoverHint': 'Εισαγωγή από το AirTrail. Μια πτήση πολλαπλών σκελών με ενδιάμεση στάση δεν αντιστοιχεί σε μία μόνο πτήση του AirTrail για συγχρονισμό, γι’ αυτό παραμένει εφάπαξ εισαγωγή.', 'reservations.airtrail.loadError': 'Δεν ήταν δυνατή η φόρτωση των πτήσεών σας από το AirTrail.', 'reservations.airtrail.imported': '{count} πτήση/πτήσεις εισήχθησαν', 'reservations.airtrail.skippedDuplicate': '{count} υπάρχουν ήδη σε αυτό το ταξίδι, παραλείφθηκαν', diff --git a/shared/src/i18n/hu/reservations.ts b/shared/src/i18n/hu/reservations.ts index 127a67acf..115d36ced 100644 --- a/shared/src/i18n/hu/reservations.ts +++ b/shared/src/i18n/hu/reservations.ts @@ -154,6 +154,7 @@ const reservations: TranslationStrings = { 'Az AirTrailből szinkronizálva — a módosítások mindkét irányban szinkronban maradnak.', 'reservations.airtrail.notSynced': 'Nincs szinkronizálva', 'reservations.airtrail.notSyncedHint': 'Ezt a járatot eltávolították az AirTrailből, és többé nem szinkronizálódik.', + 'reservations.airtrail.layoverHint': 'AirTrailből importálva. A többszakaszos, átszállásos járatnak nincs egyetlen AirTrail-járata, amellyel szinkronizálható lenne, ezért egyszeri importálás marad.', 'reservations.airtrail.loadError': 'Nem sikerült betölteni az AirTrail-járataidat.', 'reservations.airtrail.imported': '{count} járat importálva', 'reservations.airtrail.skippedDuplicate': '{count} már szerepel ebben az utazásban, kihagyva', diff --git a/shared/src/i18n/id/reservations.ts b/shared/src/i18n/id/reservations.ts index 41d5ac90a..4258c698e 100644 --- a/shared/src/i18n/id/reservations.ts +++ b/shared/src/i18n/id/reservations.ts @@ -150,6 +150,7 @@ const reservations: TranslationStrings = { 'reservations.airtrail.syncedHint': 'Tersinkron dari AirTrail — perubahan tetap sinkron di kedua arah.', 'reservations.airtrail.notSynced': 'Tidak tersinkron', 'reservations.airtrail.notSyncedHint': 'Penerbangan ini telah dihapus di AirTrail dan tidak lagi tersinkron.', + 'reservations.airtrail.layoverHint': 'Diimpor dari AirTrail. Penerbangan multi-segmen dengan transit tidak memiliki satu penerbangan AirTrail untuk disinkronkan, sehingga tetap menjadi impor sekali saja.', 'reservations.airtrail.loadError': 'Tidak dapat memuat penerbangan AirTrail-mu.', 'reservations.airtrail.imported': '{count} penerbangan diimpor', 'reservations.airtrail.skippedDuplicate': '{count} sudah ada di perjalanan ini, dilewati', diff --git a/shared/src/i18n/it/reservations.ts b/shared/src/i18n/it/reservations.ts index d5f27ea6f..8955cae9d 100644 --- a/shared/src/i18n/it/reservations.ts +++ b/shared/src/i18n/it/reservations.ts @@ -151,6 +151,7 @@ const reservations: TranslationStrings = { 'Sincronizzato da AirTrail — le modifiche restano sincronizzate in entrambe le direzioni.', 'reservations.airtrail.notSynced': 'Non sincronizzato', 'reservations.airtrail.notSyncedHint': 'Questo volo è stato rimosso in AirTrail e non si sincronizza più.', + 'reservations.airtrail.layoverHint': 'Importato da AirTrail. Un volo con più tratte e scalo non corrisponde a un singolo volo AirTrail da sincronizzare, quindi resta un\'importazione una tantum.', 'reservations.airtrail.loadError': 'Impossibile caricare i tuoi voli AirTrail.', 'reservations.airtrail.imported': '{count} volo/i importato/i', 'reservations.airtrail.skippedDuplicate': '{count} già presente/i in questo viaggio, ignorato/i', diff --git a/shared/src/i18n/ja/reservations.ts b/shared/src/i18n/ja/reservations.ts index a0e759d10..898fc47e7 100644 --- a/shared/src/i18n/ja/reservations.ts +++ b/shared/src/i18n/ja/reservations.ts @@ -150,6 +150,7 @@ const reservations: TranslationStrings = { 'reservations.airtrail.syncedHint': 'AirTrail と同期済み — 編集は双方向で同期されます。', 'reservations.airtrail.notSynced': '未同期', 'reservations.airtrail.notSyncedHint': 'このフライトは AirTrail で削除されたため、同期されなくなりました。', + 'reservations.airtrail.layoverHint': 'AirTrail からインポートしました。乗り継ぎのある複数区間のフライトは、同期先となる単一の AirTrail フライトがないため、一度限りのインポートのままになります。', 'reservations.airtrail.loadError': 'AirTrail のフライトを読み込めませんでした。', 'reservations.airtrail.imported': '{count} 件のフライトをインポートしました', 'reservations.airtrail.skippedDuplicate': '{count} 件はこの旅行に既に存在するためスキップしました', diff --git a/shared/src/i18n/ko/reservations.ts b/shared/src/i18n/ko/reservations.ts index bec314b94..4cd928bbe 100644 --- a/shared/src/i18n/ko/reservations.ts +++ b/shared/src/i18n/ko/reservations.ts @@ -150,6 +150,7 @@ const reservations: TranslationStrings = { 'reservations.airtrail.syncedHint': 'AirTrail에서 동기화됨 — 수정 사항이 양방향으로 동기화됩니다.', 'reservations.airtrail.notSynced': '동기화되지 않음', 'reservations.airtrail.notSyncedHint': '이 항공편은 AirTrail에서 삭제되어 더 이상 동기화되지 않습니다.', + 'reservations.airtrail.layoverHint': 'AirTrail에서 가져왔습니다. 경유가 있는 여러 구간 항공편은 동기화할 단일 AirTrail 항공편이 없으므로 일회성 가져오기로 유지됩니다.', 'reservations.airtrail.loadError': 'AirTrail 항공편을 불러올 수 없습니다.', 'reservations.airtrail.imported': '{count}개 항공편을 가져왔습니다', 'reservations.airtrail.skippedDuplicate': '{count}개는 이미 이 여행에 있어 건너뛰었습니다', diff --git a/shared/src/i18n/nl/reservations.ts b/shared/src/i18n/nl/reservations.ts index 174a7093f..a698b65d6 100644 --- a/shared/src/i18n/nl/reservations.ts +++ b/shared/src/i18n/nl/reservations.ts @@ -152,6 +152,7 @@ const reservations: TranslationStrings = { 'Gesynchroniseerd vanuit AirTrail — wijzigingen blijven beide kanten op gesynchroniseerd.', 'reservations.airtrail.notSynced': 'Niet gesynchroniseerd', 'reservations.airtrail.notSyncedHint': 'Deze vlucht is in AirTrail verwijderd en wordt niet meer gesynchroniseerd.', + 'reservations.airtrail.layoverHint': 'Geïmporteerd uit AirTrail. Een vlucht met meerdere trajecten en overstap heeft geen enkele AirTrail-vlucht om mee te synchroniseren en blijft daarom een eenmalige import.', 'reservations.airtrail.loadError': 'Je AirTrail-vluchten konden niet worden geladen.', 'reservations.airtrail.imported': '{count} vlucht(en) geïmporteerd', 'reservations.airtrail.skippedDuplicate': '{count} al in deze reis, overgeslagen', diff --git a/shared/src/i18n/pl/reservations.ts b/shared/src/i18n/pl/reservations.ts index 122ba3167..11cef88de 100644 --- a/shared/src/i18n/pl/reservations.ts +++ b/shared/src/i18n/pl/reservations.ts @@ -151,6 +151,7 @@ const reservations: TranslationStrings = { 'reservations.airtrail.syncedHint': 'Zsynchronizowano z AirTrail — zmiany są synchronizowane w obie strony.', 'reservations.airtrail.notSynced': 'Niezsynchronizowane', 'reservations.airtrail.notSyncedHint': 'Ten lot został usunięty w AirTrail i nie jest już synchronizowany.', + 'reservations.airtrail.layoverHint': 'Zaimportowano z AirTrail. Lot wieloetapowy z przesiadką nie ma pojedynczego lotu AirTrail do synchronizacji, więc pozostaje jednorazowym importem.', 'reservations.airtrail.loadError': 'Nie udało się wczytać Twoich lotów z AirTrail.', 'reservations.airtrail.imported': 'Zaimportowano {count} lot(y/ów)', 'reservations.airtrail.skippedDuplicate': '{count} już w tej wyprawie, pominięto', diff --git a/shared/src/i18n/ru/reservations.ts b/shared/src/i18n/ru/reservations.ts index 0e8551956..ecc6a1d14 100644 --- a/shared/src/i18n/ru/reservations.ts +++ b/shared/src/i18n/ru/reservations.ts @@ -150,6 +150,7 @@ const reservations: TranslationStrings = { 'reservations.airtrail.syncedHint': 'Синхронизировано с AirTrail — изменения синхронизируются в обе стороны.', 'reservations.airtrail.notSynced': 'Не синхронизировано', 'reservations.airtrail.notSyncedHint': 'Этот рейс был удалён в AirTrail и больше не синхронизируется.', + 'reservations.airtrail.layoverHint': 'Импортировано из AirTrail. Многосегментный рейс с пересадкой не имеет одного рейса AirTrail для синхронизации, поэтому остаётся разовым импортом.', 'reservations.airtrail.loadError': 'Не удалось загрузить ваши рейсы из AirTrail.', 'reservations.airtrail.imported': 'Импортировано рейсов: {count}', 'reservations.airtrail.skippedDuplicate': '{count} уже в этой поездке, пропущено', diff --git a/shared/src/i18n/sv/reservations.ts b/shared/src/i18n/sv/reservations.ts index 99d0e07f4..efeb494bc 100644 --- a/shared/src/i18n/sv/reservations.ts +++ b/shared/src/i18n/sv/reservations.ts @@ -151,6 +151,7 @@ const reservations: TranslationStrings = { 'reservations.airtrail.syncedHint': 'Synkroniserat från AirTrail – ändringarna synkroniseras åt båda hållen.', 'reservations.airtrail.notSynced': 'Ej synkroniserad', 'reservations.airtrail.notSyncedHint': 'Denna flygning har tagits bort i AirTrail och synkroniseras inte längre.', + 'reservations.airtrail.layoverHint': 'Importerad från AirTrail. En flerdelad flygning med mellanlandning har ingen enskild AirTrail-flygning att synkronisera mot och förblir därför en engångsimport.', 'reservations.airtrail.loadError': 'Det gick inte att hämta dina AirTrail-flygningar.', 'reservations.airtrail.imported': '{count} flygning(ar) importerades', 'reservations.airtrail.skippedDuplicate': '{count} redan under den här resan, hoppade över', diff --git a/shared/src/i18n/tr/reservations.ts b/shared/src/i18n/tr/reservations.ts index 7233ed552..bbaad9415 100644 --- a/shared/src/i18n/tr/reservations.ts +++ b/shared/src/i18n/tr/reservations.ts @@ -152,6 +152,7 @@ const reservations: TranslationStrings = { 'AirTrail ile senkronize edildi — düzenlemeler iki yönlü olarak senkronize kalır.', 'reservations.airtrail.notSynced': 'Senkronize değil', 'reservations.airtrail.notSyncedHint': "Bu uçuş AirTrail'de kaldırıldı ve artık senkronize edilmiyor.", + 'reservations.airtrail.layoverHint': 'AirTrail\'den içe aktarıldı. Aktarmalı, çok bacaklı bir uçuşun eşitlenecek tek bir AirTrail uçuşu yoktur, bu nedenle tek seferlik içe aktarma olarak kalır.', 'reservations.airtrail.loadError': 'AirTrail uçuşlarınız yüklenemedi.', 'reservations.airtrail.imported': '{count} uçuş içe aktarıldı', 'reservations.airtrail.skippedDuplicate': '{count} zaten bu gezide, atlandı', diff --git a/shared/src/i18n/uk/reservations.ts b/shared/src/i18n/uk/reservations.ts index ba6c56334..4ab434c15 100644 --- a/shared/src/i18n/uk/reservations.ts +++ b/shared/src/i18n/uk/reservations.ts @@ -150,6 +150,7 @@ const reservations: TranslationStrings = { 'reservations.airtrail.syncedHint': 'Синхронізовано з AirTrail — зміни синхронізуються в обидва боки.', 'reservations.airtrail.notSynced': 'Не синхронізовано', 'reservations.airtrail.notSyncedHint': 'Цей рейс було видалено в AirTrail і він більше не синхронізується.', + 'reservations.airtrail.layoverHint': 'Імпортовано з AirTrail. Багатосегментний рейс із пересадкою не має єдиного рейсу AirTrail для синхронізації, тому залишається одноразовим імпортом.', 'reservations.airtrail.loadError': 'Не вдалося завантажити ваші рейси з AirTrail.', 'reservations.airtrail.imported': '{count} рейс(ів) імпортовано', 'reservations.airtrail.skippedDuplicate': '{count} вже в цій подорожі, пропущено', diff --git a/shared/src/i18n/vi/reservations.ts b/shared/src/i18n/vi/reservations.ts index 8842c9929..fbd4a9b67 100644 --- a/shared/src/i18n/vi/reservations.ts +++ b/shared/src/i18n/vi/reservations.ts @@ -153,6 +153,7 @@ const reservations: TranslationStrings = { 'Đã đồng bộ hóa từ AirTrail — các chỉnh sửa vẫn được đồng bộ hóa theo cả hai cách.', 'reservations.airtrail.notSynced': 'Chưa được đồng bộ hóa', 'reservations.airtrail.notSyncedHint': 'Chuyến bay này đã bị xóa trong AirTrail và không còn đồng bộ hóa nữa.', + 'reservations.airtrail.layoverHint': 'Đã nhập từ AirTrail. Chuyến bay nhiều chặng có điểm nối chuyến không có một chuyến bay AirTrail duy nhất để đồng bộ, nên vẫn là bản nhập một lần.', 'reservations.airtrail.loadError': 'Không thể tải chuyến bay AirTrail của bạn.', 'reservations.airtrail.imported': '{count} chuyến bay đã nhập', 'reservations.airtrail.skippedDuplicate': '{count} đã tham gia chuyến đi này, đã bỏ qua', diff --git a/shared/src/i18n/zh-TW/reservations.ts b/shared/src/i18n/zh-TW/reservations.ts index bab1ca046..23e419645 100644 --- a/shared/src/i18n/zh-TW/reservations.ts +++ b/shared/src/i18n/zh-TW/reservations.ts @@ -149,6 +149,7 @@ const reservations: TranslationStrings = { 'reservations.airtrail.syncedHint': '已從 AirTrail 同步——編輯會雙向保持同步。', 'reservations.airtrail.notSynced': '未同步', 'reservations.airtrail.notSyncedHint': '此航班已在 AirTrail 中移除,不再同步。', + 'reservations.airtrail.layoverHint': '從 AirTrail 匯入。含轉機的多段航班沒有可供同步的單一 AirTrail 航班,因此保留為一次性匯入。', 'reservations.airtrail.loadError': '無法載入你的 AirTrail 航班。', 'reservations.airtrail.imported': '已匯入 {count} 筆航班', 'reservations.airtrail.skippedDuplicate': '{count} 筆已在此行程中,已略過', diff --git a/shared/src/i18n/zh/reservations.ts b/shared/src/i18n/zh/reservations.ts index d2cc86e7f..9cfd6e357 100644 --- a/shared/src/i18n/zh/reservations.ts +++ b/shared/src/i18n/zh/reservations.ts @@ -149,6 +149,7 @@ const reservations: TranslationStrings = { 'reservations.airtrail.syncedHint': '已从 AirTrail 同步——编辑会双向保持同步。', 'reservations.airtrail.notSynced': '未同步', 'reservations.airtrail.notSyncedHint': '此航班已在 AirTrail 中删除,不再同步。', + 'reservations.airtrail.layoverHint': '从 AirTrail 导入。含中转的多段航班没有对应的单个 AirTrail 航班可供同步,因此保留为一次性导入。', 'reservations.airtrail.loadError': '无法加载您的 AirTrail 航班。', 'reservations.airtrail.imported': '已导入 {count} 个航班', 'reservations.airtrail.skippedDuplicate': '{count} 个已在此行程中,已跳过',