From 06f46439c08dec274eb0b0ebb012400caaac2935 Mon Sep 17 00:00:00 2001 From: David Schuchert <142668300+DavidSchuchert@users.noreply.github.com> Date: Fri, 10 Apr 2026 03:35:23 +0200 Subject: [PATCH] feat: add German translation and make UI elements translatable (#190) Co-authored-by: David Work --- static/i18n.js | 236 ++++++++++++++++++++++++++++++++++++++++++++++ static/index.html | 116 +++++++++++------------ 2 files changed, 294 insertions(+), 58 deletions(-) diff --git a/static/i18n.js b/static/i18n.js index 4f69703..2df80f8 100644 --- a/static/i18n.js +++ b/static/i18n.js @@ -136,6 +136,232 @@ const LOCALES = { login_btn: 'Sign in', login_invalid_pw: 'Invalid password', login_conn_failed: 'Connection failed', + // Sidebar & Tabs + tab_chat: 'Chat', + tab_tasks: 'Tasks', + tab_skills: 'Skills', + tab_memory: 'Memory', + tab_workspaces: 'Spaces', + tab_profiles: 'Profiles', + tab_todos: 'Todos', + new_conversation: 'New conversation', + filter_conversations: 'Filter conversations...', + scheduled_jobs: 'Scheduled jobs', + new_job: 'New job', + loading: 'Loading...', + search_skills: 'Search skills...', + new_skill: 'New skill', + personal_memory: 'Personal memory', + current_task_list: 'Current task list', + workspace_desc: 'Add and switch workspaces for your sessions.', + new_profile: 'New profile', + transcript: 'Transcript', + download_transcript: 'Download as Markdown', + import: 'Import', + // Settings detail + settings_label_sound: 'Notification sound', + settings_desc_sound: 'Play a sound when the assistant finishes a response.', + settings_label_notifications: 'Browser notifications', + settings_desc_notifications: 'Show a system notification when a response completes while the tab is in the background.', + settings_desc_token_usage: 'Displays input/output token count below each assistant reply. Also toggled with /usage.', + settings_desc_cli_sessions: 'Merges sessions from the Hermes CLI (state.db) into the session list. Click a CLI session to import it and continue the conversation.', + settings_desc_sync_insights: 'Mirrors WebUI token usage to state.db so hermes /insights includes browser session data. Off by default.', + settings_desc_check_updates: 'Show a banner when newer versions of the WebUI or Agent are available. Runs a background git fetch periodically.', + settings_desc_bot_name: 'Display name for the assistant throughout the UI. Defaults to Hermes.', + settings_desc_password: 'Enter a new password to set or change it. Leave blank to keep current setting.', + password_placeholder: 'Enter new password…', + disable_auth: 'Disable Auth', + sign_out: 'Sign Out', + cancel: 'Cancel', + create_job: 'Create job', + save_skill: 'Save skill', + editing: 'Editing', + // Empty state + empty_title: 'What can I help with?', + empty_subtitle: 'Ask anything, run commands, explore files, or manage your scheduled tasks.', + suggest_files: 'What files are in this workspace?', + suggest_schedule: "What's on my schedule today?", + suggest_plan: 'Help me plan a small project.', + }, + + de: { + _lang: 'de', + _label: 'Deutsch', + _speech: 'de-DE', + // boot.js + cancelling: 'Abbrechen\u2026', + cancel_failed: 'Abbrechen fehlgeschlagen: ', + mic_denied: 'Mikrofonzugriff verweigert. Überprüfen Sie die Browserberechtigungen.', + mic_no_speech: 'Keine Sprache erkannt. Versuchen Sie es erneut.', + mic_network: 'Spracherkennung nicht verfügbar.', + mic_error: 'Spracheingabefehler: ', + session_imported: 'Sitzung importiert', + import_failed: 'Import fehlgeschlagen: ', + import_invalid_json: 'Ungültiges JSON', + image_pasted: 'Bild eingefügt: ', + // messages.js + edit_message: 'Nachricht bearbeiten', + regenerate: 'Antwort regenerieren', + copy: 'Kopieren', + copied: 'Kopiert!', + you: 'Du', + thinking: 'Nachdenken', + expand_all: 'Alle ausklappen', + collapse_all: 'Alle einklappen', + edit_failed: 'Bearbeiten fehlgeschlagen: ', + regen_failed: 'Regeneration fehlgeschlagen: ', + reconnect_active: 'Eine Antwort wird noch generiert. Neu laden, wenn bereit?', + reconnect_finished: 'Eine Antwort war in Arbeit, als Sie zuletzt gegangen sind. Nachrichten könnten aktualisiert worden sein.', + // approval card + approval_heading: 'Genehmigung erforderlich', + approval_desc_prefix: 'Gefährlicher Befehl erkannt', + approval_btn_once: 'Einmal zulassen', + approval_btn_once_title: 'Diesen einen Befehl zulassen (Enter)', + approval_btn_session: 'Sitzung zulassen', + approval_btn_session_title: 'Für diese Konversationssitzung zulassen', + approval_btn_always: 'Immer zulassen', + approval_btn_always_title: 'Dieses Befehlsmuster immer zulassen', + approval_btn_deny: 'Ablehnen', + approval_btn_deny_title: 'Ablehnen \u2014 diesen Befehl nicht ausführen', + approval_responding: 'Antwortet\u2026', + untitled: 'Unbenannt', + n_messages: (n) => `${n} Nachrichten`, + model_unavailable: ' (nicht verfügbar)', + model_unavailable_title: 'Dieses Modell ist nicht mehr in Ihrer aktuellen Provider-Liste', + // commands.js + cmd_help: 'Verfügbare Befehle auflisten', + cmd_clear: 'Konversationsverlauf löschen', + cmd_compact: 'Kontext komprimieren', + cmd_model: 'Modell wechseln (z.B. /model gpt-4o)', + cmd_workspace: 'Workspace nach Namen wechseln', + cmd_new: 'Neue Chat-Sitzung starten', + cmd_usage: 'Token-Verbrauchsanzeige umschalten', + cmd_theme: 'Theme wechseln (dark/light/slate/solarized/monokai/nord/oled)', + cmd_personality: 'Agenten-Persönlichkeit wechseln', + available_commands: 'Verfügbare Befehle:', + type_slash: 'Tippe / für Befehle', + conversation_cleared: 'Konversation gelöscht', + model_usage: 'Nutzung: /model ', + no_model_match: 'Kein Modell gefunden für "', + switched_to: 'Gewechselt zu ', + workspace_usage: 'Nutzung: /workspace ', + no_workspace_match: 'Kein Workspace gefunden für "', + switched_workspace: 'Gewechselt zu Workspace: ', + workspace_switch_failed: 'Workspace-Wechsel fehlgeschlagen: ', + new_session: 'Neue Sitzung erstellt', + compressing: 'Kontext-Komprimierung wird angefordert...', + token_usage_on: 'Token-Verbrauch an', + token_usage_off: 'Token-Verbrauch aus', + theme_usage: 'Nutzung: /theme ', + theme_set: 'Theme: ', + no_active_session: 'Keine aktive Sitzung', + no_personalities: 'Keine Persönlichkeiten gefunden (füge sie in ~/.hermes/personalities/ hinzu)', + available_personalities: 'Verfügbare Persönlichkeiten:', + personality_switch_hint: '\n\nNutze `/personality ` zum Wechseln, oder `/personality none` zum Löschen.', + personalities_load_failed: 'Fehler beim Laden der Persönlichkeiten', + personality_cleared: 'Persönlichkeit gelöscht', + personality_set: 'Persönlichkeit: ', + failed_colon: 'Fehlgeschlagen: ', + // ui.js + no_workspace: 'Kein Workspace', + // workspace.js + unsaved_confirm: 'Sie haben ungespeicherte Änderungen in der Vorschau. Verwerfen und fortfahren?', + save: 'Speichern', + edit: 'Bearbeiten', + save_title: 'Änderungen speichern', + edit_title: 'Diese Datei bearbeiten', + saved: 'Gespeichert', + save_failed: 'Speichern fehlgeschlagen: ', + image_load_failed: 'Bild konnte nicht geladen werden', + file_open_failed: 'Datei konnte nicht geöffnet werden', + downloading: (name) => `Lade ${name} herunter\u2026`, + double_click_rename: 'Doppelklick zum Umbenennen', + renamed_to: 'Umbenannt in ', + rename_failed: 'Umbenennen fehlgeschlagen: ', + delete_title: 'Löschen', + delete_confirm: (name) => `${name} löschen?`, + deleted: 'Gelöscht ', + delete_failed: 'Löschen fehlgeschlagen: ', + new_file_prompt: 'Neuer Dateiname (z.B. notes.md):', + created: 'Erstellt ', + create_failed: 'Erstellen fehlgeschlagen: ', + new_folder_prompt: 'Neuer Ordnername:', + folder_created: 'Ordner erstellt ', + folder_create_failed: 'Ordner erstellen fehlgeschlagen: ', + remove_title: 'Entfernen', + empty_dir: '(leer)', + upload_failed: 'Upload fehlgeschlagen: ', + all_uploads_failed: (n) => `Alle ${n} Upload(s) fehlgeschlagen`, + // settings panel + settings_title: 'Einstellungen', + settings_save_btn: 'Einstellungen speichern', + settings_label_model: 'Standard-Modell', + settings_label_send_key: 'Sende-Taste', + settings_label_theme: 'Theme', + settings_label_language: 'Sprache', + settings_label_token_usage: 'Token-Verbrauch anzeigen', + settings_label_cli_sessions: 'CLI-Sitzungen anzeigen', + settings_label_sync_insights: 'Mit Insights synchronisieren', + settings_label_check_updates: 'Nach Updates suchen', + settings_label_bot_name: 'Assistenten-Name', + settings_label_password: 'Zugangspasswort', + settings_saved: 'Einstellungen gespeichert', + settings_save_failed: 'Speichern fehlgeschlagen: ', + settings_load_failed: 'Laden der Einstellungen fehlgeschlagen: ', + settings_saved_pw: 'Einstellungen gespeichert (Passwort gesetzt \u2014 Login jetzt erforderlich)', + // login page + login_title: 'Anmelden', + login_subtitle: 'Geben Sie Ihr Passwort ein, um fortzufahren', + login_placeholder: 'Passwort', + login_btn: 'Anmelden', + login_invalid_pw: 'Ungültiges Passwort', + login_conn_failed: 'Verbindung fehlgeschlagen', + // Sidebar & Tabs + tab_chat: 'Chat', + tab_tasks: 'Aufgaben', + tab_skills: 'Skills', + tab_memory: 'Gedächtnis', + tab_workspaces: 'Spaces', + tab_profiles: 'Profile', + tab_todos: 'Todos', + new_conversation: 'Neuer Chat', + filter_conversations: 'Chats filtern...', + scheduled_jobs: 'Geplante Aufgaben', + new_job: 'Neuer Job', + loading: 'Lädt...', + search_skills: 'Skills suchen...', + new_skill: 'Neuer Skill', + personal_memory: 'Persönliches Gedächtnis', + current_task_list: 'Aktuelle Aufgabenliste', + workspace_desc: 'Workspaces hinzufügen und wechseln.', + new_profile: 'Neues Profil', + transcript: 'Protokoll', + download_transcript: 'Als Markdown herunterladen', + import: 'Importieren', + // Settings detail + settings_label_sound: 'Benachrichtigungston', + settings_desc_sound: 'Spielt einen Ton ab, wenn der Assistent eine Antwort beendet.', + settings_label_notifications: 'Browser-Benachrichtigungen', + settings_desc_notifications: 'Zeigt eine Systembenachrichtigung an, wenn eine Antwort fertiggestellt wird, während der Tab im Hintergrund ist.', + settings_desc_token_usage: 'Zeigt die Anzahl der Input/Output-Token unter jeder Antwort des Assistenten an. Auch umschaltbar mit /usage.', + settings_desc_cli_sessions: 'Fügt Sitzungen aus der Hermes CLI (state.db) in die Sitzungsliste ein. Klicken Sie auf eine CLI-Sitzung, um sie zu importieren.', + settings_desc_sync_insights: 'Spiegelt den WebUI-Token-Verbrauch in die state.db, sodass hermes /insights Browser-Sitzungsdaten enthält. Standardmäßig aus.', + settings_desc_check_updates: 'Zeigt ein Banner an, wenn neuere Versionen der WebUI oder des Agenten verfügbar sind.', + settings_desc_bot_name: 'Anzeigename für den Assistenten in der UI. Standardmäßig Hermes.', + settings_desc_password: 'Geben Sie ein neues Passwort ein, um es zu setzen oder zu ändern. Leer lassen, um die aktuelle Einstellung beizubehalten.', + password_placeholder: 'Neues Passwort eingeben…', + disable_auth: 'Authentifizierung deaktivieren', + sign_out: 'Abmelden', + cancel: 'Abbrechen', + create_job: 'Job erstellen', + save_skill: 'Skill speichern', + editing: 'Bearbeite', + // Empty state + empty_title: 'Wie kann ich helfen?', + empty_subtitle: 'Frage mich alles, führe Befehle aus oder verwalte deine Aufgaben.', + suggest_files: 'Welche Dateien sind in diesem Workspace?', + suggest_schedule: 'Was steht heute auf meinem Plan?', + suggest_plan: 'Hilf mir, ein kleines Projekt zu planen.', }, zh: { @@ -321,6 +547,16 @@ function applyLocaleToDOM() { const val = t(key); if (val && val !== key) el.textContent = val; }); + document.querySelectorAll('[data-i18n-title]').forEach(el => { + const key = el.getAttribute('data-i18n-title'); + const val = t(key); + if (val && val !== key) el.title = val; + }); + document.querySelectorAll('[data-i18n-placeholder]').forEach(el => { + const key = el.getAttribute('data-i18n-placeholder'); + const val = t(key); + if (val && val !== key) el.placeholder = val; + }); } // Apply saved locale immediately so there's no flash of English on reload. diff --git a/static/index.html b/static/index.html index 43c6776..ca7c11e 100644 --- a/static/index.html +++ b/static/index.html @@ -16,30 +16,30 @@