From d4521c87c6de1411ff30475c802d740690831d06 Mon Sep 17 00:00:00 2001 From: Remi <73385395+itsrubberduck@users.noreply.github.com> Date: Fri, 19 Sep 2025 08:54:57 +0200 Subject: [PATCH] Translate pilot monitoring and learn pages --- app/components/CookieConsentBanner.vue | 32 ++++---- app/pages/pm.vue | 105 ++++++++++++------------- shared/data/learnModules.ts | 34 ++++---- shared/utils/communicationsEngine.ts | 46 +++++------ 4 files changed, 107 insertions(+), 110 deletions(-) diff --git a/app/components/CookieConsentBanner.vue b/app/components/CookieConsentBanner.vue index 0a7234f..81f1ba6 100644 --- a/app/components/CookieConsentBanner.vue +++ b/app/components/CookieConsentBanner.vue @@ -10,28 +10,27 @@ > @@ -66,7 +64,7 @@ diff --git a/app/pages/pm.vue b/app/pages/pm.vue index fc1ddef..7a136cd 100644 --- a/app/pages/pm.vue +++ b/app/pages/pm.vue @@ -22,7 +22,7 @@

VATSIM Integration

-

Gib deine CID ein um deine gefilterten Flugpläne zu laden

+

Enter your CID to load your filtered flight plans.

- Flugpläne abrufen + Load flight plans

Demo Mode

-

Starte mit einem Beispiel-Flugplan zum Testen

+

Start with a sample flight plan for testing.

- Demo starten (DLH39A EDDF→EDDM) + Start demo (DLH39A EDDF→EDDM)
@@ -80,20 +80,20 @@ mdi-arrow-left -

Verfügbare Flugpläne

+

Available flight plans

{{ vatsimId }}
-

Lade Flugpläne von VATSIM...

+

Loading flight plans from VATSIM…

mdi-airplane-off -

Keine Flugpläne gefunden

+

No flight plans found

- Zurück + Back
@@ -135,7 +135,7 @@
-

Aktiver Flug

+

Active flight

{{ flightContext.callsign || 'N/A' }}

{{ flightContext.dep }} → {{ flightContext.dest }}

@@ -203,7 +203,7 @@ @click="backToSetup" prepend-icon="mdi-airplane-off" > - Neuen Flug wählen + Select new flight @@ -247,7 +247,7 @@ @click="swapFrequencies" :class="{ 'swap-animation': swapAnimation }" > - Frequenzen tauschen + Swap frequencies @@ -270,7 +270,7 @@
-

Erwartete Kommunikation

+

Expected communication

{{ currentStep.frequencyName }}
@@ -285,7 +285,7 @@
mdi-account-pilot - Pilot (Du) + Pilot (You)

{{ normalizeExpectedText(currentStep.pilot) }}

@@ -314,9 +314,9 @@ class="bg-cyan-500/10 text-cyan-100 mb-4" density="compact" > - Mikrofonberechtigung erforderlich für Push-To-Talk. + Microphone permission required for push-to-talk. @@ -344,7 +344,7 @@ {{ radioMode === 'atc' ? 'ATC' : 'INTERCOM' }}

- {{ radioMode === 'atc' ? `Sendet auf ${frequencies.active}` : 'Crew Intercom • Checklist' }} + {{ radioMode === 'atc' ? `Transmitting on ${frequencies.active}` : 'Crew Intercom • Checklist' }}

@@ -376,7 +376,7 @@ prepend-icon="mdi-information-variant" density="comfortable" > - ATIS abhören + Listen to ATIS - Voller Flug (Simulation) + Full flight (simulation)
@@ -396,7 +396,7 @@
-

Frequenzübersicht

+

Frequency overview

- Frequenzen für {{ flightContext.dep || 'Abflug' }} + Frequencies for {{ flightContext.dep || 'departure' }}
- Lade Frequenzen vom Netzwerk… + Loading frequencies from the network…
- Keine Frequenzen verfügbar. Bitte später erneut versuchen. + No frequencies available. Please try again later.

- Für Notfälle wenn PTT nicht funktioniert oder für Tests + For emergencies when PTT fails or for testing

@@ -546,7 +546,7 @@
- {{ simulationRunning ? 'Läuft' : 'Bereit' }} + {{ simulationRunning ? 'Running' : 'Ready' }}
@@ -564,7 +564,7 @@ v-if="simulationRunning && simulationTrace.length === 0" class="text-sm text-white/60" > - Simulation initialisiert... + Simulation initializing...
-

Aktueller Knoten

+

Current node

{{ debugState?.id || '—' }}

{{ debugState ? `${debugState.role} • ${debugState.phase}` : 'N/A' }} ({{ debugState.frequencyName }})

- Auto: {{ debugState.sayPlain }} + Auto (LLM): {{ debugState.sayPlain }}

- Funk: {{ debugState.sayNormalized }} + Radio: {{ debugState.sayNormalized }}

-

Nächste Entscheidungen

+

Upcoming decisions

{{ state.sayPlain }}

- Funk: {{ state.sayNormalized }} + Radio: {{ state.sayNormalized }}

-

Keine weiteren Entscheidungen verfügbar.

+

No further decisions available.

@@ -671,7 +671,7 @@ :disabled="log.length === 0" @click="clearLog" > - Leeren + Clear
@@ -702,7 +702,7 @@

- Noch keine Kommunikation aufgezeichnet. + No communications recorded yet.

@@ -730,7 +730,7 @@ class="text-xs uppercase tracking-[0.3em]" :class="lastTransmissionFaulty ? 'text-red-300' : 'text-cyan-300'" > - Letzte Übertragung + Last transmission
@@ -740,7 +740,7 @@ color="red" variant="flat" > - Fehlerhaft + Faulty mdi-alert-circle-outline - {{ lastTransmissionFaulty ? 'Fehler bearbeiten' : 'Fehler markieren' }} + {{ lastTransmissionFaulty ? 'Edit issue' : 'Mark as faulty' }} mdi-restore - Zurücksetzen + Reset mdi-close - Löschen + Delete
@@ -786,12 +786,12 @@ v-if="lastTransmissionFaulty" class="space-y-2 rounded-xl border border-red-400/30 bg-red-500/10 px-3 py-2" > -

Fehlerbeschreibung

+

Issue description

{{ lastTransmissionFaultNote }}

- Als fehlerhaft markiert. + Marked as faulty.

@@ -801,16 +801,15 @@ - Übertragung als fehlerhaft markieren + Mark transmission as faulty

- Hinterlasse optional eine kurze Beschreibung, damit das Dev Team die - Übertragung nachvollziehen kann. + Optionally leave a short note so the dev team can follow up on the transmission.

- Abbrechen + Cancel - Markierung entfernen + Remove flag - Speichern + Save
@@ -844,7 +843,7 @@
{ flightPlans.value = response.slice(0, 10) currentScreen.value = 'flightselect' } else { - error.value = 'Keine Flugpläne für diese VATSIM ID gefunden' + error.value = 'No flight plans found for this VATSIM ID' } } catch (err) { console.error('Error loading flight plans:', err) - error.value = 'Fehler beim Laden der Flugpläne. Bitte VATSIM ID prüfen.' + error.value = 'Error loading flight plans. Please check the VATSIM ID.' } finally { loading.value = false } @@ -1947,7 +1946,7 @@ const playAtisBroadcast = async () => { } if (!content) { - setLastTransmission('ATIS: Keine aktuellen Informationen verfügbar') + setLastTransmission('ATIS: No current information available') return } diff --git a/shared/data/learnModules.ts b/shared/data/learnModules.ts index 42c15f2..1d75745 100644 --- a/shared/data/learnModules.ts +++ b/shared/data/learnModules.ts @@ -5,7 +5,7 @@ export type Lesson = { id: string; title: string; desc: string; target: string; export type ModuleDef = { id: string; title: string; subtitle: string; art: string; lessons: Lesson[] } const modules = ref([ - // NEW: ICAO Kapitel + // New: ICAO chapter { id: 'icao', title: 'ICAO Alphabet', @@ -15,33 +15,33 @@ const modules = ref([ { id: 'alpha', title: 'Alphabet A–M', - desc: 'Alpha bis Mike sprechen.', + desc: 'Say Alpha through Mike.', target: 'Alpha Bravo Charlie Delta Echo Foxtrot Golf Hotel India Juliett Kilo Lima Mike.', - hints: ['konstant sprechen', 'deutlich trennen'], + hints: ['Keep a steady pace', 'Separate each word clearly'], keywords: ['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot', 'Golf', 'Hotel', 'India', 'Juliett', 'Kilo', 'Lima', 'Mike'] }, { id: 'alpha2', title: 'Alphabet N–Z', - desc: 'November bis Zulu.', + desc: 'November through Zulu.', target: 'November Oscar Papa Quebec Romeo Sierra Tango Uniform Victor Whiskey X-ray Yankee Zulu.', - hints: ['X-ray mit hyphen', 'Juliett mit zwei t'], + hints: ['Spell X-ray with a hyphen', "Juliett has two t's"], keywords: ['November', 'Oscar', 'Papa', 'Quebec', 'Romeo', 'Sierra', 'Tango', 'Uniform', 'Victor', 'Whiskey', 'X-ray', 'Yankee', 'Zulu'] }, { id: 'numbers', - title: 'Zahlen', - desc: 'ICAO-Nummernlesen.', + title: 'Numbers', + desc: 'Read ICAO numbers.', target: 'Tree Fower Fife Six Seven Eight Niner Zero.', hints: ['Nine → Niner', 'Three → Tree', 'Four → Fower', 'Five → Fife'], keywords: ['Tree', 'Fower', 'Fife', 'Niner'] }, { id: 'callsign-icao', - title: 'Callsign Buchstabieren', - desc: 'Beispiel-Callsign.', + title: 'Spell the call sign', + desc: 'Sample call sign.', target: 'DLH one two three, Lufthansa one two three.', - hints: ['DLH → Lufthansa', 'Nummern ICAO'], + hints: ['DLH → Lufthansa', 'Use ICAO numbers'], keywords: ['Lufthansa', 'DLH', 'one', 'two', 'three'] } ] @@ -49,19 +49,19 @@ const modules = ref([ { id: 'basics', title: 'Basics', - subtitle: 'Callsign · Struktur · Zahlen', + subtitle: 'Call sign · Structure · Numbers', art: 'https://images.unsplash.com/photo-1541392822270-85b2ff6c4577?q=80&w=1600&auto=format&fit=crop', lessons: [ { id: 'checkin', title: 'Check-in', - desc: 'Erster Call korrekt.', + desc: 'Make the first call correctly.', target: 'Frankfurt Ground, Lufthansa one two three at stand A12, request taxi.', hints: ['Station • Callsign • Position • Intent'], keywords: ['Frankfurt Ground', 'Lufthansa', 'stand', 'request taxi'] }, { - id: 'readback', title: 'Short Readback', desc: 'Kurz bestätigen.', + id: 'readback', title: 'Short Readback', desc: 'Give a short acknowledgement.', target: 'Lufthansa one two three, roger.', hints: ['Callsign + roger/affirm'], keywords: ['roger', 'affirm'] } @@ -79,7 +79,7 @@ const modules = ref([ hints: ['Taxi to runway • via • hold short'], keywords: ['taxi to runway', 'via', 'hold short'] }, { - id: 'handoff', title: 'Handoff', desc: 'Frequenzwechsel.', + id: 'handoff', title: 'Handoff', desc: 'Frequency change.', target: 'Contact Tower on one one niner decimal five, Lufthansa one two three.', hints: ['Contact Tower on … • decimal'], keywords: ['Contact Tower', 'decimal'] } @@ -92,7 +92,7 @@ const modules = ref([ art: 'https://images.unsplash.com/photo-1494412685616-a5d310fbb07d?q=80&w=1600&auto=format&fit=crop', lessons: [ { - id: 'lineup', title: 'Line up', desc: 'Aufrollen und warten.', + id: 'lineup', title: 'Line up', desc: 'Line up and wait.', target: 'Lufthansa one two three, line up and wait runway two five.', hints: ['line up and wait'], keywords: ['line up and wait'] } @@ -105,7 +105,7 @@ const modules = ref([ art: 'https://images.unsplash.com/photo-1542089363-07b2d92aacc3?q=80&w=1600&auto=format&fit=crop', lessons: [ { - id: 'vacate', title: 'Vacate', desc: 'Verlasse Bahn, melde frei.', + id: 'vacate', title: 'Vacate', desc: 'Exit the runway and report clear.', target: 'Lufthansa one two three, vacated runway two five via A six.', hints: ['vacated runway • via taxiway'], keywords: ['vacated', 'runway'] } @@ -118,7 +118,7 @@ const modules = ref([ art: 'https://images.unsplash.com/photo-1508264769638-658b34d79f6e?q=80&w=1600&auto=format&fit=crop', lessons: [ { - id: 'checkin', title: 'IFR Check-in', desc: 'Erster Online-Call.', + id: 'checkin', title: 'IFR Check-in', desc: 'First online call.', target: 'Frankfurt Ground, Lufthansa one two three, A320 at stand A12, IFR to Munich, information Bravo, request clearance.', hints: ['IFR/VFR • ATIS Info • Request'], keywords: ['IFR', 'information', 'request clearance'] } diff --git a/shared/utils/communicationsEngine.ts b/shared/utils/communicationsEngine.ts index c1eb26b..2234135 100644 --- a/shared/utils/communicationsEngine.ts +++ b/shared/utils/communicationsEngine.ts @@ -1,8 +1,8 @@ -// composables/communicationsEngine.ts +// communicationsEngine composable import { ref, computed, readonly } from 'vue' import atcDecisionTree from "../data/atcDecisionTree"; -// --- DecisionTree-Types (aus ~/data/atcDecisionTree.json abgeleitet) --- +// --- DecisionTree types (derived from ~/data/atcDecisionTree.json) --- type Role = 'pilot' | 'atc' | 'system' type Phase = | 'Preflight' | 'Clearance' | 'PushStart' | 'TaxiOut' | 'Departure' @@ -31,8 +31,8 @@ interface DTState { auto?: 'check_readback' | 'monitor' | 'end' | 'pop_stack_or_route_by_intent' readback_required?: string[] actions?: (DTActionSet | string)[] - frequency?: string // Für einfachere Freq-Zugriffe - frequencyName?: string // Name der Frequenz (DEL, GND, TWR, etc.) + frequency?: string // Shortcut for accessing frequencies + frequencyName?: string // Frequency label (DEL, GND, TWR, etc.) } interface DecisionTree { @@ -47,8 +47,8 @@ interface DecisionTree { emergency_active: boolean current_unit: string stack: string[] - off_schema_count: number // Zähler für Off-Schema Antworten - radio_checks_done: number // Zähler für Radio Checks + off_schema_count: number // Counter for off-schema responses + radio_checks_done: number // Counter for radio checks } policies: { timeouts: { @@ -67,7 +67,7 @@ interface DecisionTree { states: Record } -// Flight Phases und Communication Steps für bessere Integration +// Flight phases and communication steps for better integration export const FLIGHT_PHASES = [ { id: 'clearance', name: 'Clearance Delivery', frequency: '121.900', action: 'Request IFR clearance' }, { id: 'ground', name: 'Ground Control', frequency: '121.700', action: 'Request pushback and taxi' }, @@ -90,7 +90,7 @@ export const COMMUNICATION_STEPS = [ atc: '{callsign}, cleared to {dest} via {sid} departure, runway {runway}, climb {initial_altitude_ft} feet, squawk {squawk}.', pilotResponse: '{callsign} cleared {dest} via {sid}, runway {runway}, climb {initial_altitude_ft}, squawk {squawk}.' } - // Weitere Steps können hier definiert werden + // Additional steps can be defined here ] type FrequencyVariableKey = 'atis_freq' @@ -101,7 +101,7 @@ type FrequencyVariableKey = 'atis_freq' | 'approach_freq' | 'handoff_freq' -// --- ATC Decision Tree laden --- +// --- Load ATC decision tree --- export interface FlightContext { callsign: string aircraft: string @@ -140,7 +140,7 @@ export interface EngineLog { offSchema?: boolean } -// NATO/ICAO Normalizer (gekürzt für bessere Performance) +// NATO/ICAO normalizer trimmed for better performance const NATO_PHONETIC: Record = { A: 'Alpha', B: 'Bravo', C: 'Charlie', D: 'Delta', E: 'Echo', F: 'Foxtrot', G: 'Golf', H: 'Hotel', I: 'India', J: 'Juliett', K: 'Kilo', L: 'Lima', @@ -212,7 +212,7 @@ export default function useCommunicationsEngine() { const currentStateId = ref(tree.value.start_state) const communicationLog = ref([]) - // Flight Context für bessere Integration mit pm_alt.vue Stil + // Flight context used for pm_alt.vue integration const flightContext = ref({ callsign: '', aircraft: 'A320', @@ -264,7 +264,7 @@ export default function useCommunicationsEngine() { } }) - // Current Step für pm_alt.vue Integration + // Current step for pm_alt.vue integration const currentStep = computed(() => { const phase = flightContext.value.phase const step = COMMUNICATION_STEPS.find(s => s.phase === phase) @@ -280,7 +280,7 @@ export default function useCommunicationsEngine() { }) function initializeFlight(fpl: any) { - // Variablen setzen + // Set variables variables.value = { ...variables.value, callsign: fpl.callsign || fpl.callsign, @@ -313,7 +313,7 @@ export default function useCommunicationsEngine() { time_now: new Date().toISOString() } - // Flight Context aktualisieren + // Update flight context Object.assign(flightContext.value, { ...variables.value, phase: 'clearance' @@ -364,11 +364,11 @@ export default function useCommunicationsEngine() { } function applyLLMDecision(decision: any) { - // Updates anwenden + // Apply updates if (decision.updates) Object.assign(variables.value, decision.updates) if (decision.flags) Object.assign(flags.value, decision.flags) - // Off-Schema und Radio Check Tracking + // Track off-schema responses and radio checks if (decision.off_schema) { flags.value.off_schema_count++ console.log(`[Engine] Off-schema response #${flags.value.off_schema_count}`) @@ -378,7 +378,7 @@ export default function useCommunicationsEngine() { console.log(`[Engine] Radio check #${flags.value.radio_checks_done}`) } - // Controller Response + // Controller response if (decision.controller_say_tpl) { speak('atc', decision.controller_say_tpl, currentStateId.value, { radioCheck: decision.radio_check, @@ -386,7 +386,7 @@ export default function useCommunicationsEngine() { }) } - // State Transition (nur wenn nicht Radio Check) + // State transition (only when not a radio check) if (!decision.radio_check) { moveTo(decision.next_state) } @@ -396,7 +396,7 @@ export default function useCommunicationsEngine() { // Log pilot input speak('pilot', transcript, currentStateId.value) - // Radio Check Detection (fallback falls LLM es nicht erkennt) + // Radio check detection (fallback if the LLM misses it) const t = transcript.toLowerCase() if (t.includes('radio check') || (t.includes('read') && t.includes('check'))) { const callsign = variables.value.callsign || '' @@ -417,7 +417,7 @@ export default function useCommunicationsEngine() { return null } - return null // LLM soll entscheiden + return null // Let the LLM decide } function processUserTransmission(transcript: string): string | null { @@ -437,7 +437,7 @@ export default function useCommunicationsEngine() { currentStateId.value = stateId const s = currentState.value - // Actions ausführen + // Execute actions for (const act of s.actions ?? []) { if (typeof act === 'string') continue if (act.if && !safeEvalBoolean(act.if)) continue @@ -457,7 +457,7 @@ export default function useCommunicationsEngine() { speak(s.role, s.say_tpl, s.id!) } - // Phase Update für Flight Context + // Update flight context phase updateFlightPhase(s.phase) } @@ -599,7 +599,7 @@ export default function useCommunicationsEngine() { communicationLog: readonly(communicationLog), clearCommunicationLog: () => { communicationLog.value = [] }, - // pm_alt.vue Integration + // pm_alt.vue integration flightContext: readonly(flightContext), currentStep,