UI in English

Made-with: Cursor
This commit is contained in:
2026-03-06 19:49:38 +01:00
parent c90d2627df
commit e238c429e5
6 changed files with 75 additions and 75 deletions

View File

@@ -12,7 +12,7 @@ use tokio::net::TcpStream as TokioTcpStream;
/// Prüft die TCP-Verbindung zum Companion (synchron, kurzer Timeout).
pub fn check_connection(host: &str, port: u16) -> Result<(), String> {
if host.trim().is_empty() {
return Err("Host leer".to_string());
return Err("Host empty".to_string());
}
let addrs: Vec<_> = (host.trim(), port)
.to_socket_addrs()
@@ -20,7 +20,7 @@ pub fn check_connection(host: &str, port: u16) -> Result<(), String> {
.collect();
let addr = addrs
.first()
.ok_or_else(|| "Host konnte nicht aufgelöst werden".to_string())?;
.ok_or_else(|| "Host could not be resolved".to_string())?;
TcpStream::connect_timeout(addr, Duration::from_secs(2)).map_err(|e| e.to_string())?;
Ok(())
}
@@ -40,7 +40,7 @@ pub async fn read_frame(stream: &mut TokioTcpStream) -> std::io::Result<Option<(
if n < 5 {
return Err(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"Unvollständiger Companion-Frame-Header",
"Incomplete companion frame header",
));
}
@@ -50,7 +50,7 @@ pub async fn read_frame(stream: &mut TokioTcpStream) -> std::io::Result<Option<(
if length > 10 * 1024 * 1024 {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidData,
"Ungültige Companion-Frame-Länge",
"Invalid companion frame length",
));
}
@@ -61,7 +61,7 @@ pub async fn read_frame(stream: &mut TokioTcpStream) -> std::io::Result<Option<(
if r == 0 {
return Err(std::io::Error::new(
std::io::ErrorKind::UnexpectedEof,
"Verbindung während Companion-Frame abgebrochen",
"Connection closed during companion frame",
));
}
offset += r;

View File

@@ -19,7 +19,7 @@ fn show_error(msg: &str) {
.encode_wide()
.chain(std::iter::once(0))
.collect();
let title: Vec<u16> = OsStr::new("HotKeet Fehler")
let title: Vec<u16> = OsStr::new("HotKeet Error")
.encode_wide()
.chain(std::iter::once(0))
.collect();
@@ -35,7 +35,7 @@ fn show_error(msg: &str) {
#[cfg(not(windows))]
fn show_error(msg: &str) {
eprintln!("Fehler: {}", msg);
eprintln!("Error: {}", msg);
}
#[cfg(windows)]
@@ -181,11 +181,11 @@ impl eframe::App for AppState {
.status
.read()
.map(|s| match *s {
AppStatus::Bereit => "Bereit",
AppStatus::Aufnahme => "Aufnahme",
AppStatus::Transkribieren => "Transkribieren",
AppStatus::Fertig => "Fertig",
AppStatus::Fehler => "Fehler",
AppStatus::Bereit => "Ready",
AppStatus::Aufnahme => "Recording",
AppStatus::Transkribieren => "Transcribing",
AppStatus::Fertig => "Done",
AppStatus::Fehler => "Error",
})
.unwrap_or("?");
let detail = self
@@ -198,8 +198,8 @@ impl eframe::App for AppState {
.read()
.ok()
.and_then(|o| *o)
.map(|t| format!("vor {} s", t.elapsed().as_secs()))
.unwrap_or_else(|| "noch nie empfangen".to_string());
.map(|t| format!("{} s ago", t.elapsed().as_secs()))
.unwrap_or_else(|| "never received".to_string());
let companion_status = self
.companion_status
@@ -252,7 +252,7 @@ impl eframe::App for AppState {
&self.status,
&self.status_detail,
AppStatus::Fertig,
&format!("Eingefügt: {}", preview),
&format!("Pasted: {}", preview),
);
}
Err(e) => {
@@ -261,7 +261,7 @@ impl eframe::App for AppState {
&self.status,
&self.status_detail,
AppStatus::Fehler,
&format!("Einfügen: {}", e),
&format!("Paste: {}", e),
);
}
}
@@ -405,12 +405,12 @@ fn main() -> eframe::Result<()> {
|| err_str.to_lowercase().contains("adapter")
{
"\n\nWindows Server ohne GPU:\n\
- opengl32.dll von Mesa3D (fdossena.com/mesa) neben die EXE legen\n\
- oder App auf Arbeitsplatz-PC mit GPU ausführen"
- Place opengl32.dll from Mesa3D (fdossena.com/mesa) next to the EXE\n\
- Or run app on a workstation PC with GPU"
} else {
""
};
show_error(&format!("Start fehlgeschlagen: {}{}", e, hint));
show_error(&format!("Start failed: {}{}", e, hint));
std::process::exit(1);
}
Ok(())
@@ -437,7 +437,7 @@ fn run_recording(
if config.sound_on_start_end {
sound::play_start(config.debug_logging);
}
set_status(&status, &status_detail, AppStatus::Aufnahme, "Aufnahme läuft");
set_status(&status, &status_detail, AppStatus::Aufnahme, "Recording");
let use_companion = config.use_companion_microphone && !config.companion_host.is_empty();
if config.debug_logging {
@@ -463,7 +463,7 @@ fn run_recording(
Ok(s) => s,
Err(e) => {
eprintln!("Aufnahme fehlgeschlagen: {}", e);
set_status(&status, &status_detail, AppStatus::Fehler, &format!("Aufnahme: {}", e));
set_status(&status, &status_detail, AppStatus::Fehler, &format!("Recording: {}", e));
return;
}
};
@@ -485,7 +485,7 @@ fn run_recording(
return;
}
set_status(&status, &status_detail, AppStatus::Transkribieren, "Transkribieren…");
set_status(&status, &status_detail, AppStatus::Transkribieren, "Transcribing");
if config.sound_on_start_end {
sound::play_end(config.debug_logging);
@@ -508,7 +508,7 @@ fn run_recording(
Err(e) => {
eprintln!("Transkription: {}", e);
let _ = std::fs::remove_file(&wav_path);
set_status(&status, &status_detail, AppStatus::Fehler, &format!("Transkription: {}", e));
set_status(&status, &status_detail, AppStatus::Fehler, &format!("Transcription: {}", e));
return;
}
};
@@ -541,6 +541,6 @@ fn run_recording(
};
if paste_tx.send(req).is_err() {
eprintln!("Paste-Kanal geschlossen");
set_status(&status, &status_detail, AppStatus::Fehler, "Paste-Kanal fehlgeschlagen");
set_status(&status, &status_detail, AppStatus::Fehler, "Paste channel failed");
}
}

View File

@@ -13,7 +13,7 @@ use tokio::net::TcpStream;
/// Eintrag für die Eingabequellen-Auswahl: (source_index, Anzeigename)
/// source_index 0 = Companion-App, 1.. = Mikrofon (input_device_index = source_index - 1)
pub fn list_input_sources() -> Vec<(usize, String)> {
let mut sources = vec![(0, "Companion-App".to_string())];
let mut sources = vec![(0, "Companion app".to_string())];
let devices = match cpal::default_host().input_devices() {
Ok(d) => d,
@@ -21,8 +21,8 @@ pub fn list_input_sources() -> Vec<(usize, String)> {
};
for (idx, device) in devices.enumerate() {
let name = device.name().unwrap_or_else(|_| format!("Mikrofon {}", idx));
sources.push((idx + 1, format!("Mikrofon: {}", name)));
let name = device.name().unwrap_or_else(|_| format!("Microphone {}", idx));
sources.push((idx + 1, format!("Microphone: {}", name)));
}
sources
}
@@ -35,8 +35,8 @@ pub fn resolve_input_device_index(config: &DictateConfig) -> usize {
Err(_) => return config.input_device_index,
};
for (idx, device) in devices.enumerate() {
let name = device.name().unwrap_or_else(|_| format!("Mikrofon {}", idx));
let display = format!("Mikrofon: {}", name);
let name = device.name().unwrap_or_else(|_| format!("Microphone {}", idx));
let display = format!("Microphone: {}", name);
if display == config.input_device_name {
return idx;
}
@@ -92,14 +92,14 @@ pub fn record_local(
let device_index = resolve_input_device_index(config);
let device = host
.input_devices()
.map_err(|e| format!("Kein Audiogerät: {}", e))?
.map_err(|e| format!("No audio device: {}", e))?
.nth(device_index)
.or_else(|| host.default_input_device())
.ok_or("Kein Eingabegerät gefunden")?;
.ok_or("No input device found")?;
let supported = device
.default_input_config()
.map_err(|e| format!("Kein Input-Config: {}", e))?;
.map_err(|e| format!("No input config: {}", e))?;
let sample_rate = supported.sample_rate();
let collector = Arc::new(SampleCollector::new());
@@ -143,7 +143,7 @@ pub fn record_local(
)
.map_err(|e| format!("Stream build: {}", e))?
}
_ => return Err("Nur I16/F32 unterstützt".to_string()),
_ => return Err("Only I16/F32 supported".to_string()),
};
stream.play().map_err(|e| format!("Stream play: {}", e))?;
@@ -195,7 +195,7 @@ pub async fn record_companion(
let addr = format!("{}:{}", host, port);
let mut stream = TcpStream::connect(&addr)
.await
.map_err(|e| format!("Companion verbinden: {}", e))?;
.map_err(|e| format!("Companion connect: {}", e))?;
let mut all_audio: Vec<i16> = Vec::new();
let start = std::time::Instant::now();
@@ -233,10 +233,10 @@ pub fn write_wav(path: &std::path::Path, samples: &[i16]) -> Result<(), String>
sample_format: hound::SampleFormat::Int,
};
let mut writer = hound::WavWriter::create(path, spec)
.map_err(|e| format!("WAV erstellen: {}", e))?;
.map_err(|e| format!("Create WAV: {}", e))?;
for &s in samples {
writer.write_sample(s).map_err(|e| format!("WAV schreiben: {}", e))?;
writer.write_sample(s).map_err(|e| format!("Write WAV: {}", e))?;
}
writer.finalize().map_err(|e| format!("WAV finalisieren: {}", e))?;
writer.finalize().map_err(|e| format!("Finalize WAV: {}", e))?;
Ok(())
}

View File

@@ -43,15 +43,15 @@ pub fn transcribe(
let output = cmd
.output()
.map_err(|e| format!("parakeet-cli starten: {}", e))?;
.map_err(|e| format!("Start parakeet-cli: {}", e))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(format!("parakeet-cli Fehler: {}", stderr));
return Err(format!("parakeet-cli error: {}", stderr));
}
let stdout = String::from_utf8(output.stdout)
.map_err(|_| "parakeet-cli Ausgabe ist kein UTF-8")?;
.map_err(|_| "parakeet-cli output is not UTF-8")?;
let json: serde_json::Value = serde_json::from_str(stdout.trim())
.map_err(|e| format!("parakeet-cli JSON: {}", e))?;

View File

@@ -32,12 +32,12 @@ pub fn create_tray(tx: Sender<TrayMessage>) -> Option<tray_item::TrayItem> {
.ok()?;
let tx_show = tx.clone();
tray.add_menu_item("Einstellungen", move || {
tray.add_menu_item("Settings", move || {
let _ = tx_show.send(TrayMessage::ShowSettings);
})
.ok()?;
tray.add_menu_item("Beenden", move || {
tray.add_menu_item("Quit", move || {
let _ = tx.send(TrayMessage::Quit);
})
.ok()?;

View File

@@ -72,7 +72,7 @@ impl SettingsApp {
companion_status: &Option<Result<(), String>>,
) {
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("HotKeet Einstellungen");
ui.heading("HotKeet Settings");
ui.add_space(8.0);
@@ -84,10 +84,10 @@ impl SettingsApp {
ui.label(status_detail);
}
});
ui.label(format!("Letzter Hotkey: {}", hotkey_info));
ui.label(format!("Last hotkey: {}", hotkey_info));
if let Some(ref tx) = self.test_tx {
if ui.button("Test: Aufnahme starten (3 s)").clicked() {
if ui.button("Test: Start recording (3 s)").clicked() {
let _ = tx.send(());
}
}
@@ -97,7 +97,7 @@ impl SettingsApp {
ui.horizontal(|ui| {
ui.label("Hotkey:");
let label = if self.hotkey_capturing {
"Taste drücken… (Esc = Abbrechen)"
"Press key… (Esc = Cancel)"
} else {
&self.config.global_hotkey
};
@@ -119,7 +119,7 @@ impl SettingsApp {
hotkey::format_hotkey(mods.ctrl, mods.shift, mods.alt, key.name())
{
self.config.global_hotkey = s;
self.status = "Änderungen speichern nicht vergessen.".to_string();
self.status = "Remember to save changes.".to_string();
self.hotkey_capturing = false;
}
// Nur bei gültiger Haupttaste abbrechen, sonst weiter (Modifier überspringen)
@@ -134,7 +134,7 @@ impl SettingsApp {
ui.add_space(4.0);
ui.label("Eingabequelle:");
ui.label("Input source:");
let sources = list_input_sources();
let mut selected = self.selected_source_index(&sources);
let selected = &mut selected;
@@ -164,11 +164,11 @@ impl SettingsApp {
);
});
match companion_status {
None => ui.label("Prüfe Verbindung"),
Some(Ok(())) => ui.label("Verbunden"),
None => ui.label("Checking connection"),
Some(Ok(())) => ui.label("Connected"),
Some(Err(e)) => ui.colored_label(
egui::Color32::RED,
format!("Nicht verbunden: {}", e),
format!("Not connected: {}", e),
),
};
}
@@ -176,87 +176,87 @@ impl SettingsApp {
ui.add_space(4.0);
ui.horizontal(|ui| {
ui.label("parakeet-cli Pfad:");
ui.label("parakeet-cli path:");
let display = if self.config.parakeet_cli_path.is_empty() {
"(leer = im PATH)".to_string()
"(empty = in PATH)".to_string()
} else {
self.config.parakeet_cli_path.clone()
};
ui.label(egui::RichText::new(&display).color(egui::Color32::GRAY));
if ui.button("Durchsuchen").clicked() {
let mut dialog = rfd::FileDialog::new().set_title("parakeet-cli auswählen");
if ui.button("Browse").clicked() {
let mut dialog = rfd::FileDialog::new().set_title("Select parakeet-cli");
#[cfg(windows)]
{
dialog = dialog.add_filter("Executable", &["exe"]);
}
dialog = dialog.add_filter("Alle Dateien", &["*"]);
dialog = dialog.add_filter("All files", &["*"]);
if let Some(p) = dialog.pick_file() {
self.config.parakeet_cli_path = p.display().to_string();
self.status = "Änderungen speichern nicht vergessen.".to_string();
self.status = "Remember to save changes.".to_string();
}
}
if !self.config.parakeet_cli_path.is_empty() && ui.small_button("").clicked() {
self.config.parakeet_cli_path.clear();
self.status = "Änderungen speichern nicht vergessen.".to_string();
self.status = "Remember to save changes.".to_string();
}
});
ui.horizontal(|ui| {
ui.label("Modellpfad:");
ui.label("Model path:");
let display = if self.config.model_path.is_empty() {
"(leer = Standardpfad)".to_string()
"(empty = default path)".to_string()
} else {
self.config.model_path.clone()
};
ui.label(egui::RichText::new(&display).color(egui::Color32::GRAY));
if ui.button("Durchsuchen").clicked() {
if ui.button("Browse").clicked() {
if let Some(p) = rfd::FileDialog::new()
.set_title("Modellordner auswählen")
.set_title("Select model folder")
.pick_folder()
{
self.config.model_path = p.display().to_string();
self.status = "Änderungen speichern nicht vergessen.".to_string();
self.status = "Remember to save changes.".to_string();
}
}
if !self.config.model_path.is_empty() && ui.small_button("").clicked() {
self.config.model_path.clear();
self.status = "Änderungen speichern nicht vergessen.".to_string();
self.status = "Remember to save changes.".to_string();
}
});
ui.add_space(4.0);
ui.horizontal(|ui| {
ui.label("Einfügemethode:");
ui.label("Paste method:");
let mut method = self.config.paste_method.clone();
egui::ComboBox::from_id_salt("paste_method")
.selected_text(&method)
.show_ui(ui, |ui| {
ui.selectable_value(&mut method, "Auto".to_string(), "Auto (Tastaturpuffer, Fallback Clipboard)");
ui.selectable_value(&mut method, "Keyboard".to_string(), "Nur Tastaturpuffer");
ui.selectable_value(&mut method, "Clipboard".to_string(), "Nur Zwischenablage");
ui.selectable_value(&mut method, "Auto".to_string(), "Auto (keyboard buffer, fallback clipboard)");
ui.selectable_value(&mut method, "Keyboard".to_string(), "Keyboard only");
ui.selectable_value(&mut method, "Clipboard".to_string(), "Clipboard only");
});
self.config.paste_method = method;
});
ui.add_space(4.0);
ui.checkbox(&mut self.config.start_minimized, "Beim Start minimieren");
ui.checkbox(&mut self.config.minimize_to_tray, "In Tray minimieren");
ui.checkbox(&mut self.config.sound_on_start_end, "Signaltöne bei Start und Ende des Diktats");
ui.checkbox(&mut self.config.debug_logging, "Debug-Logging (paste-debug.log, Konsole)");
ui.checkbox(&mut self.config.start_minimized, "Minimize on start");
ui.checkbox(&mut self.config.minimize_to_tray, "Minimize to tray");
ui.checkbox(&mut self.config.sound_on_start_end, "Audio feedback on record start/end");
ui.checkbox(&mut self.config.debug_logging, "Debug logging (paste-debug.log, console)");
ui.add_space(16.0);
if ui.button("Speichern").clicked() {
if ui.button("Save").clicked() {
match self.config.save() {
Ok(()) => {
if let Some(ref arc) = self.config_arc {
let _ = arc.write().map(|mut w| *w = self.config.clone());
}
self.status = "Gespeichert.".to_string();
self.status = "Saved.".to_string();
}
Err(e) => self.status = format!("Fehler: {}", e),
Err(e) => self.status = format!("Error: {}", e),
}
}