from __future__ import annotations
from sqlalchemy.orm import Session
from .models import Language, Translation

BASE_TRANSLATIONS = {
"app.dashboard": ("Dashboard", "Übersicht"), "app.assets": ("Assets", "Assets"), "app.asset_tree": ("Asset tree", "Baumstruktur"),
"app.charts": ("Charts", "Diagramme"), "app.categories": ("Categories", "Kategorien"), "app.status_values": ("Status values", "Statuswerte"),
"app.settings": ("Settings", "Einstellungen"), "app.users": ("Users", "Benutzer"), "app.profile": ("Profile", "Profil"),
"app.login": ("Sign in", "Anmelden"), "app.logout": ("Sign out", "Ausloggen"), "app.guest": ("Guest", "Gast"),
"auth.disabled_banner_title": ("Authentication is disabled", "Authentifizierung ist deaktiviert"),
"auth.disabled_banner_text": ("This installation is currently accessible without signing in. For the first setup, edit data/config/config.json, set authentication.mode to local, restart the app container, and then sign in with the protected local administrator configured in .env.", "Diese Installation ist derzeit ohne Anmeldung erreichbar. Für die Ersteinrichtung bearbeite data/config/config.json, setze authentication.mode auf local, starte den App-Container neu und melde dich anschließend mit dem in .env konfigurierten geschützten lokalen Administrator an."),
"common.save": ("Save", "Speichern"), "common.cancel": ("Cancel", "Abbrechen"), "common.delete": ("Delete", "Löschen"),
"common.edit": ("Edit", "Bearbeiten"), "common.search": ("Search", "Suchen"), "common.export": ("Export", "Exportieren"),
"common.import": ("Import", "Importieren"), "common.back": ("Back", "Zurück"), "common.yes": ("Yes", "Ja"), "common.no": ("No", "Nein"),
"common.active": ("Active", "Aktiv"), "common.actions": ("Actions", "Aktionen"), "common.name": ("Name", "Name"),
"common.description": ("Description", "Beschreibung"), "common.language": ("Language", "Sprache"), "common.all": ("All", "Alle"),

"fields.title": ("Fields", "Felder"),
"fields.add": ("Add field", "Feld hinzufügen"),
"fields.edit": ("Edit field", "Feld bearbeiten"),
"fields.catalog_help": ("Central labels and data types apply immediately to all categories, forms, tables and exports.", "Zentrale Bezeichnungen und Datentypen gelten sofort für alle Kategorien, Formulare, Tabellen und Exporte."),
"fields.field": ("Field", "Feld"),
"fields.label": ("Label", "Bezeichnung"),
"fields.data_type": ("Data type", "Datentyp"),
"fields.read_only": ("Read only", "Nur lesen"),
"fields.system": ("System", "System"),
"fields.stored_values": ("Stored values", "Gespeicherte Werte"),
"fields.delete_confirm": ("Delete this field and all values?", "Dieses Feld und alle gespeicherten Werte löschen?"),
"fields.confirm_values": ("Confirm values", "Werte bestätigen"),
"fields.technical_name": ("Technical field name", "Technischer Feldname"),
"fields.technical_name_help": ("Letters, numbers and underscores only.", "Nur Buchstaben, Zahlen und Unterstriche."),
"fields.automatic": ("automatic", "automatisch"),
"fields.default_field": ("Default field", "Standardfeld"),
"fields.searchable": ("Searchable", "Durchsuchbar"),
"fields.filterable": ("Filterable", "Filterbar"),
"fields.chart_enabled": ("Available for charts", "Für Diagramme verfügbar"),
"fields.excel_import": ("Excel import", "Excel-Import"),
"fields.excel_export": ("Excel export", "Excel-Export"),
"fields.bulk_edit": ("Bulk edit", "Sammelbearbeitung"),
"fields.data_type_short_text": ("Short text", "Kurztext"),
"fields.data_type_long_text": ("Long text", "Langtext"),
"fields.data_type_integer": ("Integer", "Ganzzahl"),
"fields.data_type_decimal": ("Decimal number", "Dezimalzahl"),
"fields.data_type_boolean": ("Boolean", "Ja/Nein"),
"fields.data_type_date": ("Date", "Datum"),
"fields.data_type_datetime": ("Date and time", "Datum und Uhrzeit"),
"fields.data_type_url": ("URL", "URL"),
"fields.data_type_email": ("E-mail address", "E-Mail-Adresse"),
"fields.data_type_ip": ("IP address", "IP-Adresse"),
"fields.data_type_json": ("JSON", "JSON"),
"fields.data_type_asset_reference": ("Asset reference", "Asset-Verweis"),
"fields.data_type_user_reference": ("User reference", "Benutzer-Verweis"),
"fields.data_type_status": ("Status", "Status"),
"software.settings.dedicated_listener": ("Dedicated callback listener", "Separater Callback-Zugang"),
"software.settings.dedicated_listener_active": ("Starts automatically with AssetManager", "Startet automatisch mit AssetManager"),
"software.settings.dedicated_listener_help": ("The callback-only service listens on {bind_ip}:{port}. It starts with the normal Docker Compose stack and exposes only the callback endpoint and health check, never the AssetManager web interface. Configure the public FQDN above as the callback base URL.", "Der reine Callback-Dienst lauscht auf {bind_ip}:{port}. Er startet zusammen mit dem normalen Docker-Compose-Stack und stellt ausschließlich den Callback-Endpunkt und den Healthcheck bereit, niemals die AssetManager-Weboberfläche. Trage oben den öffentlichen FQDN als Callback-Basisadresse ein."),
"profile.title": ("User profile", "Benutzerprofil"), "profile.username": ("Username", "Benutzername"),
"profile.display_name": ("Display name", "Anzeigename"), "profile.email": ("Email", "E-Mail"), "profile.source": ("Source", "Quelle"),
"profile.last_login": ("Last sign-in", "Letzte Anmeldung"), "profile.change_password": ("Change password", "Passwort ändern"),
"profile.current_password": ("Current password", "Aktuelles Passwort"), "profile.new_password": ("New password", "Neues Passwort"),
"profile.save_language": ("Save language", "Sprache speichern"),
"profile.browser_storage": ("Browser storage", "Browserspeicherung"),
"profile.browser_storage_help": ("Store table filters, column order and future interface preferences in this browser, even after it is closed.", "Tabellenfilter, Spaltenreihenfolgen und künftige Oberflächeneinstellungen in diesem Browser dauerhaft speichern."),
"profile.browser_storage_enable": ("Enable persistent browser storage", "Dauerhafte Browserspeicherung aktivieren"),
"profile.save_browser_storage": ("Save browser storage setting", "Browserspeicherung speichern"), "language.changed": ("Language changed", "Sprache geändert"),
"settings.appearance": ("Appearance", "Darstellung"), "settings.application_title": ("Application title", "Anwendungstitel"),
"settings.company_name": ("Company name", "Firmenname"), "settings.company_logo": ("Company logo", "Firmenlogo"),
"settings.remove_logo": ("Remove logo", "Logo entfernen"), "settings.authentication": ("User authentication", "Benutzeranmeldung"),
"settings.save": ("Save settings", "Einstellungen speichern"), "settings.translations": ("Translations", "Übersetzungen"),
"settings.translation_help": ("Manage interface languages and translations. English is the source and fallback language.", "Oberflächensprachen und Übersetzungen verwalten. Englisch ist Quell- und Rückfallsprache."),
"translations.title": ("Translations", "Übersetzungen"), "translations.export_excel": ("Export Excel", "Excel exportieren"),
"translations.import_excel": ("Import Excel", "Excel importieren"), "translations.only_missing": ("Only missing translations", "Nur fehlende Übersetzungen"),
"translations.key": ("Key", "Schlüssel"), "translations.english": ("English", "Englisch"), "translations.german": ("German", "Deutsch"),
"translations.import_options": ("Import options", "Importoptionen"), "translations.ignore_empty": ("Ignore empty cells", "Leere Zellen ignorieren"),
"translations.updated": ("Translations updated", "Übersetzungen aktualisiert"), "translations.imported": ("Translations imported", "Übersetzungen importiert"),
"users.management": ("User management", "Benutzerverwaltung"), "users.new": ("Create user", "Benutzer anlegen"),
"users.delete": ("Delete user", "Benutzer löschen"), "users.ldap_import": ("Import LDAP users", "LDAP-Benutzer übernehmen"),
"users.protected_local_admin": ("Protected local emergency administrator", "Geschützter lokaler Notfall-Administrator"),
"users.protected_local_admin_help": ("This account remains active as a local administrator even when LDAP authentication is enabled. It cannot be disabled or deleted.", "Dieses Konto bleibt auch bei aktivierter LDAP-Anmeldung als lokaler Administrator verfügbar. Es kann nicht deaktiviert oder gelöscht werden."),
"users.protected_delete_forbidden": ("Protected local administrators cannot be deleted.", "Geschützte lokale Administratoren können nicht gelöscht werden."),
"assets.bulk_edit": ("Edit data", "Daten ändern"), "assets.delete_selected": ("Delete selected", "Markierte löschen"),
"assets.select_all": ("Select all", "Alle markieren"), "assets.category": ("Category", "Kategorie"), "assets.status": ("Status", "Status"),
"assets.operating_system": ("Operating system", "Betriebssystem"), "assets.manufacturer": ("Manufacturer", "Hersteller"),
"assets.model": ("Model", "Modell"), "assets.location": ("Location", "Standort"), "assets.room": ("Room", "Raumnummer"),
"assets.parent": ("Parent asset", "Übergeordnetes Gerät"), "charts.manage": ("Manage charts", "Diagramme verwalten"),
"charts.create": ("Create chart", "Diagramm erstellen"), "charts.no_data": ("No data available", "Keine Daten vorhanden"),
"ldap.title": ("LDAP user search", "LDAP-Benutzersuche"), "ldap.show_all": ("Show all AD users", "Alle AD-Benutzer anzeigen"),
"ldap.save_selected": ("Save selected users", "Ausgewählte Benutzer speichern"), "ldap.query": ("Search term or wildcard", "Suchbegriff oder Wildcard"),
"login.title": ("Sign in", "Anmelden"), "login.password": ("Password", "Passwort"),

"common.assets": ("Assets", "Assets"), "common.date": ("Date", "Datum"), "common.order": ("Order", "Reihenfolge"),
"common.new": ("New", "Neu"), "common.filter": ("Filter", "Filtern"), "common.reset": ("Reset", "Zurücksetzen"),
"common.duplicate": ("Duplicate", "Duplizieren"), "common.read_only": ("Read only", "Nur lesen"),
"dashboard.overview": ("Overview", "Übersicht"), "dashboard.recently_changed": ("Recently changed", "Zuletzt geändert"),
"dashboard.list_fields_hint": ("The fields enabled as ‘Show in list’ for each category are displayed.", "Angezeigt werden die Felder, die in der jeweiligen Kategorie unter „In Liste“ aktiviert sind."),
"charts.show_all": ("Show all charts", "Alle Diagramme anzeigen"), "charts.visible_assets_hint": ("Current analyses based on the assets visible to you.", "Aktuelle Auswertungen aus den für dich sichtbaren Assets."),
"charts.none": ("No charts have been created yet.", "Noch keine Diagramme vorhanden."),
"assets.create": ("Create asset", "Asset anlegen"), "assets.export_excel": ("Export Excel", "Excel exportieren"),
"assets.import_excel": ("Import Excel", "Excel importieren"), "assets.column_order_hint": ("Columns can be moved by dragging the table header. The order is saved in this browser.", "Spalten können am Tabellenkopf verschoben werden. Die Reihenfolge wird in diesem Browser gespeichert."),
"assets.name": ("Name", "Bezeichnung"), "assets.issue": ("Issue", "Ausgeben"), "assets.return": ("Return", "Zurücknehmen"),
"assets.duplicate": ("Duplicate", "Duplizieren"), "assets.none": ("No assets available.", "Keine Assets vorhanden."),
"assets.issue_asset": ("Issue asset", "Asset ausgeben"), "assets.delete_confirm": ("Really delete asset ‘{name}’?", "Asset „{name}“ wirklich löschen?"),
"assets.no_ip": ("No IP", "keine IP"), "assets.no_location": ("No location", "kein Standort"),
"tree.root_node": ("Root node", "Oberster Knoten"), "tree.all_root_assets": ("All root assets", "Alle Wurzelgeräte"),
"tree.no_category_selection": ("No selection shows all categories.", "Keine Auswahl zeigt alle Kategorien."),
"tree.name_contains": ("Name contains", "Name enthält"), "tree.hide_standalone": ("Hide standalone assets without a parent asset", "Einzelne Geräte ohne übergeordnetes Gerät ausblenden"),
"tree.no_matches": ("No matching assets found.", "Keine passenden Geräte gefunden."),
"categories.create": ("Create category", "Kategorie anlegen"), "categories.active_fields": ("Active fields", "Aktive Felder"),
"status.help": ("Define which status is set when assets are issued and returned.", "Lege hier fest, welcher Status beim Ausgeben und Zurücknehmen gesetzt wird."),
"status.on_issue": ("On issue", "Beim Ausgeben"), "status.on_return": ("On return", "Beim Zurücknehmen"),
"status.delete_confirm": ("Really delete this status value?", "Statuswert wirklich löschen?"), "status.new_value": ("New status value", "Neuer Statuswert"),
"mesh.title": ("MeshCentral synchronization", "MeshCentral-Synchronisierung"), "mesh.start": ("Start synchronization", "Synchronisierung starten"),
"mesh.linked_assets": ("linked assets", "verknüpfte Assets"), "mesh.conflicts": ("Conflicts", "Konflikte"), "mesh.missing": ("no longer found", "nicht mehr gefunden"),
"mesh.live_log": ("Live log", "Live-Protokoll"), "mesh.ready": ("ready", "bereit"), "mesh.ready_message": ("Ready for synchronization.", "Bereit für die Synchronisierung."),
"mesh.configuration": ("Configuration", "Konfiguration"), "mesh.file": ("File", "Datei"), "mesh.user": ("User", "Benutzer"),
"mesh.verify_tls": ("Verify TLS", "TLS prüfen"), "mesh.asset_matching": ("Asset matching", "Asset-Zuordnung"),
"mesh.asset_matching_help": ("Choose which identifiers are used to match MeshCentral devices to existing assets. Matching is evaluated in the displayed order.", "Wähle, mit welchen Kennungen MeshCentral-Geräte bestehenden Assets zugeordnet werden. Die Zuordnung wird in der angezeigten Reihenfolge geprüft."),
"mesh.match_node_id": ("Node ID", "Node-ID"),
"mesh.match_mac": ("MAC address", "MAC-Adresse"),
"mesh.match_serial": ("Serial number", "Seriennummer"),
"mesh.match_mac_help": ("MAC matching is used only for an exact, unique MAC address. Node ID remains the primary device identity.", "Der MAC-Abgleich wird nur bei einer exakten, eindeutigen MAC-Adresse verwendet. Die Node-ID bleibt die primäre Geräteidentität."),
"mesh.match_serial_manufacturer": ("For serial-number matches, also compare normalized manufacturer", "Beim Seriennummern-Abgleich zusätzlich den normalisierten Hersteller vergleichen"),
"mesh.match_serial_manufacturer_help": ("When enabled, a serial-number match is accepted only if the normalized manufacturer also matches. Case, punctuation, accents and common legal suffixes such as GmbH, Inc. or Corporation are ignored.", "Wenn aktiviert, wird eine Übereinstimmung über die Seriennummer nur akzeptiert, wenn auch der normalisierte Hersteller übereinstimmt. Groß-/Kleinschreibung, Satzzeichen, Akzente und übliche Rechtsformzusätze wie GmbH, Inc. oder Corporation werden ignoriert."),
"mesh.invalid_serial_values": ("Invalid serial-number placeholders", "Ungültige Seriennummern-Platzhalter"),
"mesh.invalid_serial_values_help": ("One value per line. These values are never used for serial-number matching and never overwrite an existing asset serial number.", "Ein Wert pro Zeile. Diese Werte werden niemals für den Seriennummern-Abgleich verwendet und überschreiben keine vorhandene Asset-Seriennummer."),
"mesh.manufacturer_invalid_serial_rules": ("Manufacturer-specific serial placeholders", "Herstellerspezifische Seriennummern-Platzhalter"),
"mesh.manufacturer_invalid_serial_rules_help": ("One rule per line in the format Manufacturer | Serial number. Example: Synology | 123456789. Manufacturer matching is normalized and ignores case, punctuation and common legal suffixes.", "Eine Regel pro Zeile im Format Hersteller | Seriennummer. Beispiel: Synology | 123456789. Der Herstellervergleich wird normalisiert und ignoriert Groß-/Kleinschreibung, Satzzeichen und übliche Rechtsformzusätze."),
"mesh.node_id_identity_help": ("If a reported serial number is recognized as a placeholder, AssetManager ignores it for matching and synchronization. The MeshCentral node ID remains the stable unique device identity. The original reported value is retained in the stored MeshCentral source data when source-data storage is enabled.", "Wird eine gemeldete Seriennummer als Platzhalter erkannt, ignoriert AssetManager sie beim Abgleich und bei der Synchronisierung. Die MeshCentral-Node-ID bleibt die stabile eindeutige Geräteidentität. Der ursprünglich gemeldete Wert bleibt bei aktivierter Quelldatenspeicherung in den gespeicherten MeshCentral-Quelldaten erhalten."),
"mesh.dry_run": ("Dry run", "Dry-Run"),
"mesh.fallback_category_id": ("Fallback category ID", "Fallback-Kategorie-ID"), "mesh.device_type_mapping": ("Device type mapping", "Gerätetyp-Mapping"),
"mesh.type": ("Type", "Typ"), "mesh.recent_runs": ("Recent runs", "Letzte Läufe"), "mesh.start_time": ("Start", "Start"),
"mesh.found": ("Found", "Gefunden"), "mesh.created": ("Created", "Neu"), "mesh.changed": ("Changed", "Geändert"),
"mesh.errors": ("Errors", "Fehler"), "mesh.message": ("Message", "Meldung"), "mesh.no_runs": ("No synchronization has been run yet.", "Noch keine Synchronisierung ausgeführt."),
"mesh.running": ("Synchronization running …", "Synchronisierung läuft …"), "mesh.starting": ("starting", "startet"),
"mesh.log_load_error": ("The log could not be loaded.", "Protokoll konnte nicht geladen werden."), "mesh.start_error": ("Could not start synchronization.", "Start fehlgeschlagen."),
"mesh.finished_visible": ("Synchronization finished. The log remains visible.", "Synchronisierung beendet. Das Protokoll bleibt sichtbar."),
"settings.mesh_mapping": ("MeshCentral category mapping", "MeshCentral-Kategoriezuordnung"),
"settings.mesh_mapping_help": ("Mappings are stored using stable category IDs. Renaming a category does not interrupt synchronization. Existing assets keep their current category; mappings apply only to newly imported devices.", "Die Zuordnung wird über stabile Kategorie-IDs gespeichert. Umbenennen einer Kategorie unterbricht die Synchronisierung daher nicht mehr. Bestehende Assets behalten ihre bisherige Kategorie; die Zuordnung gilt nur für neu importierte Geräte."),
"settings.save_mesh_mapping": ("Save category mapping", "Kategoriezuordnung speichern"),
"settings.mesh_mapping_saved": ("MeshCentral category mapping saved", "MeshCentral-Kategoriezuordnung gespeichert"),
"settings.fallback_category": ("Fallback category", "Fallback-Kategorie"), "settings.mobile_device": ("Mobile device", "Mobilgerät"),
"settings.device_type": ("Device type", "Gerätetyp"), "settings.other_device": ("other device", "sonstiges Gerät"),
"settings.mesh_type": ("MeshCentral type", "MeshCentral-Typ"), "settings.use_fallback": ("Use fallback", "Fallback verwenden"),
"settings.auth_help": ("Administrators have full access. Non-administrators have read-only access to assigned MeshCentral groups.", "Administratoren haben Vollzugriff. Nicht-Administratoren erhalten ausschließlich Lesezugriff auf zugewiesene MeshCentral-Gruppen."),
"settings.auth_mode": ("Authentication mode", "Anmeldemodus"), "settings.auth_none": ("No authentication", "Keine Anmeldung"),
"settings.auth_local": ("Local users", "Lokale Benutzer"), "settings.ldap_connection": ("LDAP connection", "LDAP-Verbindung"),
"settings.ldap_server": ("LDAP server", "LDAP-Server"), "settings.port": ("Port", "Port"), "settings.use_ssl": ("Use SSL/LDAPS", "SSL/LDAPS verwenden"),
"settings.use_starttls": ("Use StartTLS", "StartTLS verwenden"), "settings.bind_user": ("Bind DN / bind user", "Bind-DN / Bind-Benutzer"),
"settings.bind_password_env": ("Environment variable for bind password", "Umgebungsvariable für Bind-Passwort"),
"settings.user_base_dn": ("User base DN", "Benutzer-Basis-DN"), "settings.user_filter": ("User filter", "Benutzerfilter"),
"settings.display_name_attribute": ("Display-name attribute", "Attribut Anzeigename"), "settings.email_attribute": ("Email attribute", "Attribut E-Mail"),
"settings.bind_password_help": ("The bind password remains outside the configuration, for example in the .env file.", "Das Bind-Passwort bleibt außerhalb der Konfiguration, zum Beispiel in der .env-Datei."),
"settings.query_ldap_users": ("Query LDAP users", "LDAP-Benutzer abfragen"), "settings.current_logo": ("Current company logo", "Aktuelles Firmenlogo"),
"users.permissions_help": ("Configure asset visibility and MeshCentral groups for non-administrators.", "Konfiguriere die Asset-Sichtbarkeit und MeshCentral-Gruppen für Nicht-Administratoren."),
"users.login_settings": ("Sign-in settings", "Anmeldeeinstellungen"), "users.create_local": ("Create local user", "Lokalen Benutzer anlegen"),
"users.administrator": ("Administrator", "Administrator"), "users.user": ("User", "Benutzer"),
"users.permissions_groups": ("Permissions and groups", "Rechte und Gruppen"), "users.visible_mesh_groups": ("Visible MeshCentral groups for non-administrators", "Sichtbare MeshCentral-Gruppen für Nicht-Administratoren"),
"users.no_mesh_groups": ("No MeshCentral groups are stored in the database yet.", "Noch keine MeshCentral-Gruppen in der Datenbank vorhanden."),
"users.save": ("Save user", "Benutzer speichern"), "users.delete_confirm": ("Really delete user ‘{username}’?", "Benutzer „{username}“ wirklich löschen?"),
"users.none": ("No users available yet.", "Noch keine Benutzer vorhanden."), "users.relogin_hint": ("After permissions are changed, the affected user must sign out and sign in again.", "Nach einer Rechteänderung muss sich der betreffende Benutzer einmal ab- und wieder anmelden."),
"users.asset_visibility": ("Asset visibility", "Asset-Sichtbarkeit"),
"users.asset_visibility_help": ("Select one or more visibility rules. All enabled rules and MeshCentral groups are combined with OR.", "Wähle eine oder mehrere Sichtbarkeitsregeln. Alle aktiven Regeln und MeshCentral-Gruppen werden mit ODER verknüpft."),
"users.scope_self": ("Only own assets", "Nur eigene Geräte"),
"users.scope_department": ("All assets in own department", "Alle Geräte der eigenen Abteilung"),
"users.scope_location": ("All assets at own location", "Alle Geräte des eigenen Standorts"),
"users.scope_all": ("All assets", "Alle Geräte"),
"users.scope_or_hint": ("Own assets match when the Assigned to field equals the username. Department and location are compared with the employee profile below.", "Eigene Geräte werden erkannt, wenn das Feld „Zugeordnet“ dem Benutzernamen entspricht. Abteilung und Standort werden mit dem nachfolgenden Mitarbeiterprofil verglichen."),
"users.profile_department": ("Employee department", "Abteilung des Mitarbeiters"),
"users.profile_location": ("Employee location", "Standort des Mitarbeiters"),
"users.profile_department_placeholder": ("e.g. IT", "z. B. IT"),
"users.profile_location_placeholder": ("e.g. Headquarters", "z. B. Hauptverwaltung"),
"field.asset_tag": ("Asset tag", "Inventarnummer"), "field.name": ("Name", "Bezeichnung"),
"field.manufacturer": ("Manufacturer", "Hersteller"), "field.model": ("Model", "Modell"),
"field.serial_number": ("Serial number", "Seriennummer"), "field.status": ("Status", "Status"),
"field.assigned_to": ("Assigned to", "Zugeordnet an"), "field.assigned_on": ("Issued on", "Ausgegeben am"),
"field.location": ("Location", "Standort"), "field.room_number": ("Room number", "Raumnummer"),
"field.parent_asset_id": ("Parent asset", "Übergeordnetes Gerät"), "field.department": ("Department", "Abteilung"),
"field.purchase_date": ("Purchase date", "Kaufdatum"), "field.warranty_until": ("Warranty until", "Garantie bis"),
"field.notes": ("Notes", "Notizen"), "field.hostname": ("Hostname", "Computername"),
"field.operating_system": ("Operating system", "Betriebssystem"), "field.os_patch_level": ("OS patch level", "OS-Patchlevel"),
"field.mac_address": ("MAC address", "MAC-Adresse"), "field.ip_address": ("IP address", "IP-Adresse"),
"field.cpu": ("CPU", "CPU"), "field.memory_gb": ("Memory (GB)", "Arbeitsspeicher (GB)"),
"field.storage_gb": ("Total storage (GB)", "Speicher gesamt (GB)"), "field.storage_details": ("Storage devices", "Datenträger"),
"field.gpu_name": ("Graphics adapter", "Grafikkarte"), "field.tpm_version": ("TPM version", "TPM-Version"),
"field.memory_details": ("Memory modules", "Arbeitsspeicher-Module"), "field.antivirus": ("Active antivirus", "Aktiver Virenschutz"),
"field.mesh_group": ("MeshCentral group", "MeshCentral-Gruppe"), "field.mesh_node_id": ("MeshCentral node ID", "MeshCentral Node-ID"),
"field.mesh_mtype": ("MeshCentral group type (mtype)", "MeshCentral-Gruppentyp (mtype)"),
"field.mesh_icon": ("MeshCentral device icon", "MeshCentral-Geräte-Icon"),

"common.close": ("Close", "Schließen"),
"common.loading": ("Loading…", "Lade…"),
"common.category": ("Category", "Kategorie"),
"common.select": ("select", "markieren"),
"common.read_only": ("Read only", "Nur lesen"),
"lists.record_count": ("Showing: {visible} of {total} records", "Angezeigt: {visible} von {total} Datensätzen"),
"lists.record_count_hint": ("The count reflects the currently active table filters.", "Die Anzeige berücksichtigt die aktuell gesetzten Tabellenfilter."),
"lists.filter_active": ("Filter active", "Filter aktiv"),
"lists.filters_active": ("{count} filters active", "{count} Filter aktiv"),
"lists.filter_status": ("Filtered: {visible} of {total} records", "Gefiltert: {visible} von {total} Datensätzen"),
"lists.clear_filters": ("Clear filters", "Filter zurücksetzen"),
"lists.row_number": ("Row number", "Zeilennummer"),
"lists.row_number_resize": ("Drag to change the row-number column width", "Ziehen, um die Breite der Nummernspalte zu ändern"),
"lists.column_resize": ("Drag to change the column width", "Ziehen, um die Spaltenbreite zu ändern"),
"appearance.colors_title": ("System colors", "Systemfarben"),
"appearance.colors_help": ("Configure the central colors used throughout the application.", "Konfigurieren Sie die zentral im gesamten System verwendeten Farben."),
"appearance.primary_color": ("Primary color", "Primärfarbe"),
"appearance.primary_hover_color": ("Primary hover color", "Primärfarbe bei Auswahl"),
"appearance.header_color": ("Header color", "Farbe der Kopfzeile"),
"appearance.navigation_color": ("Navigation color", "Navigationsfarbe"),
"appearance.page_background": ("Page background", "Seitenhintergrund"),
"appearance.surface_color": ("Content surface", "Inhaltsflächen"),
"appearance.text_color": ("Text color", "Textfarbe"),
"appearance.border_color": ("Border color", "Rahmenfarbe"),
"appearance.success_color": ("Success color", "Erfolgsfarbe"),
"appearance.warning_color": ("Warning color", "Warnfarbe"),
"appearance.danger_color": ("Danger color", "Fehlerfarbe"),
"appearance.reset_colors": ("Reset colors", "Farben zurücksetzen"),
"appearance.color_preview": ("Color preview", "Farbenvorschau"),
"appearance.default_theme": ("Default display mode", "Standard-Darstellungsmodus"),
"appearance.theme": ("Display mode", "Darstellungsmodus"),
"appearance.light": ("Light", "Hell"),
"appearance.dark": ("Dark", "Dunkel"),
"appearance.switch_to_light": ("Switch to light mode", "Zum hellen Modus wechseln"),
"appearance.switch_to_dark": ("Switch to dark mode", "Zum dunklen Modus wechseln"),
"appearance.theme_saved": ("Display mode saved", "Darstellungsmodus gespeichert"),
"appearance.profile_help": ("Your selection is stored in your profile and applies on every device after signing in.", "Ihre Auswahl wird im Profil gespeichert und gilt nach der Anmeldung auf jedem Gerät."),
"appearance.colors_saved": ("Appearance saved", "Darstellung gespeichert"),
"assets.bulk_edit": ("Edit data", "Daten ändern"),
"assets.delete_selected": ("Delete selected", "Markierte löschen"),
"assets.reload": ("Reload content", "Inhalt neu laden"),
"assets.export_excel": ("Export Excel", "Excel exportieren"),
"assets.import_excel": ("Import Excel", "Excel importieren"),
"assets.create": ("Create asset", "Asset anlegen"),
"assets.columns": ("Columns", "Spalten"),
"assets.columns_hint": ("Show or hide columns. The selection is stored in this browser.", "Spalten ein- oder ausblenden. Die Auswahl wird in diesem Browser gespeichert."),
"assets.show_all_columns": ("Show all", "Alle einblenden"),
"assets.reset_columns": ("Reset", "Zurücksetzen"),
"assets.hide_column": ("Hide column", "Spalte ausblenden"),
"assets.manage_columns": ("Manage columns…", "Spalten verwalten…"),
"assets.issue": ("Issue", "Ausgeben"),
"assets.return": ("Return", "Zurücknehmen"),
"assets.none": ("No assets available.", "Keine Assets vorhanden."),
"assets.import_title": ("Excel import", "Excel-Import"),
"assets.import_template": ("Download Excel template", "Excel-Vorlage herunterladen"),
"assets.import_file": ("Excel file", "Excel-Datei"),
"assets.import_only_active": ("Import only active fields", "Nur aktive Felder importieren"),
"assets.import_only_active_help": ("By default, all known fields enabled for Excel import are imported, even if they are not active or visible in the selected category. Enable this option only to restrict the import to active category fields.", "Standardmäßig werden alle bekannten und für den Excel-Import freigegebenen Felder übernommen, auch wenn sie in der jeweiligen Kategorie nicht aktiv oder sichtbar sind. Aktiviere diese Option nur, wenn der Import auf die aktiven Felder der Kategorie begrenzt werden soll."),
"assets.import_start": ("Start import", "Import starten"),
"categories.activate_default_fields": ("Activate default fields", "Standardfelder aktivieren"),
"categories.select_all": ("Select all categories", "Alle Kategorien markieren"),
"categories.fields_total": ("Total fields", "Felder gesamt"),
"categories.list_fields": ("In list", "In Liste"),
"categories.attributes": ("Active attributes", "Aktive Attribute"),
"categories.shown_in_list": ("Visible in list", "In Liste sichtbar"),
"categories.active_only": ("Active but not visible in list", "Aktiv, aber nicht in Liste sichtbar"),
"categories.no_active_fields": ("No active fields", "Keine aktiven Felder"),
"categories.in_use": ("Category is still in use", "Kategorie wird noch verwendet"),
"categories.confirm_delete": ("Delete this category? This is only possible when it is not used by any asset.", "Kategorie wirklich löschen? Das ist nur möglich, wenn sie von keinem Asset verwendet wird."),
"categories.bulk.edit": ("Edit field", "Feld ändern"),
"categories.bulk.dialog_title": ("Edit selected categories", "Markierte Kategorien ändern"),
"categories.bulk.field": ("Field", "Feld"),
"categories.bulk.value": ("New value", "Neuer Inhalt"),
"categories.bulk.selected_count": ("{count} categories selected", "{count} Kategorien ausgewählt"),
"categories.bulk.name_hint": ("The name is only available when exactly one category is selected.", "Der Name kann nur geändert werden, wenn genau eine Kategorie markiert ist."),
"categories.bulk.clear": ("Clear the selected field", "Ausgewähltes Feld leeren"),
"categories.bulk.image": ("Category image", "Kategoriebild"),
"categories.bulk.image_hint": ("The same image is assigned to all selected categories.", "Dasselbe Bild wird allen markierten Kategorien zugewiesen."),
"categories.bulk.no_selection": ("No categories selected", "Keine Kategorien ausgewählt"),
"categories.bulk.name_single_only": ("The name can only be changed for one category at a time", "Der Name kann nur für eine Kategorie gleichzeitig geändert werden"),
"categories.bulk.name_required": ("The category name must not be empty", "Der Kategoriename darf nicht leer sein"),
"categories.bulk.image_required": ("Select an image or choose clear value", "Bitte ein Bild auswählen oder das Feld leeren"),
"categories.bulk.name_duplicate": ("The category name already exists", "Der Kategoriename ist bereits vorhanden"),
"categories.bulk.updated": ("Updated {count} categories", "{count} Kategorien wurden geändert"),
"categories.bulk.unsupported_field": ("Unsupported category field", "Nicht unterstütztes Kategorienfeld"),
"categories.fields.manage": ("Manage fields", "Felder verwalten"),
"categories.fields.add": ("Add", "Hinzufügen"),
"categories.fields.remove": ("Remove", "Entfernen"),
"categories.fields.settings": ("Change settings", "Einstellungen ändern"),
"categories.fields.copy": ("Apply template", "Übernehmen"),
"categories.fields.add_help": ("The selected fields are activated in all selected categories. Existing assignments are retained.", "Die ausgewählten Felder werden in allen markierten Kategorien aktiviert. Bereits vorhandene Zuordnungen bleiben erhalten."),
"categories.fields.remove_help": ("The selected fields are deactivated in all selected categories. Existing asset data is retained. The name field cannot be removed.", "Die ausgewählten Felder werden in allen markierten Kategorien deaktiviert. Vorhandene Assetdaten bleiben erhalten. Das Namensfeld kann nicht entfernt werden."),
"categories.fields.settings_help": ("Settings are changed only for categories in which the field is active.", "Die Einstellungen werden nur für Kategorien geändert, in denen das Feld aktiv ist."),
"categories.fields.copy_help": ("The complete field assignment and settings are copied from an existing category to all selected categories. Fields not active in the template are deactivated in the target categories; asset data is retained.", "Die komplette Feldzuordnung und deren Einstellungen werden aus einer vorhandenen Kategorie auf alle markierten Kategorien übernommen. Nicht in der Vorlage aktive Felder werden in den Zielkategorien deaktiviert; Assetdaten bleiben erhalten."),
"categories.fields.source": ("Template category", "Vorlagenkategorie"),
"categories.fields.copy_warning": ("The field configuration of the selected categories is aligned with the template. Category names, descriptions, images and assets are not changed.", "Die Feldkonfiguration der markierten Kategorien wird an die Vorlage angeglichen. Kategorienamen, Beschreibungen, Bilder und Assets werden nicht verändert."),
"categories.fields.change_required": ("Change required-field setting", "Pflichtfeld-Einstellung ändern"),
"categories.fields.change_list": ("Change list display", "Listenanzeige ändern"),
"categories.fields.no_selection": ("No categories selected", "Keine Kategorien ausgewählt"),
"categories.fields.choose_fields": ("Select at least one field", "Bitte mindestens ein Feld auswählen"),
"categories.fields.choose_field": ("Select a field", "Bitte ein Feld auswählen"),
"categories.fields.choose_setting": ("Select at least one setting to change", "Bitte mindestens eine zu ändernde Einstellung auswählen"),
"categories.fields.choose_source": ("Select a source category", "Bitte eine Vorlagenkategorie auswählen"),
"categories.fields.unsupported_operation": ("Unsupported field operation", "Nicht unterstützte Feldaktion"),
"categories.fields.updated": ("Field configuration updated for {count} categories", "Feldkonfiguration bei {count} Kategorien aktualisiert"),
"settings.favicon": ("Browser tab logo (favicon)", "Browser-Tab-Logo (Favicon)"),
"settings.favicon_hint": ("Recommended: a square PNG, for example 32 × 32 or 64 × 64 pixels.", "Empfohlen: quadratisches PNG, zum Beispiel 32 × 32 oder 64 × 64 Pixel."),
"settings.current_favicon": ("Current browser tab logo", "Aktuelles Browser-Tab-Logo"),
"settings.remove_favicon": ("Remove browser tab logo", "Browser-Tab-Logo entfernen"),
"settings.about_information": ("About information", "Systeminformationen"),
"settings.about_information_help": ("These values are shown in the About dialog. The application version is read from the VERSION file and cannot be edited here.", "Diese Angaben werden im Info-Dialog angezeigt. Die Anwendungsversion wird aus der Datei VERSION gelesen und kann hier nicht bearbeitet werden."),
"settings.about_author": ("Created by / author", "Erstellt von / Autor"),
"settings.about_organization": ("Organization", "Organisation"),
"settings.about_contact": ("Contact", "Kontakt"),
"settings.about_website": ("Website", "Webseite"),
"settings.about_repository": ("Source repository", "Quellcode-Repository"),
"settings.about_license": ("License", "Lizenz"),
"settings.about_copyright": ("Copyright notice", "Copyright-Hinweis"),
"settings.version_from_file": ("Version from VERSION file", "Version aus der Datei VERSION"),
"about.open": ("About this application", "Informationen zu dieser Anwendung"),
"about.version": ("Version", "Version"),
"about.author": ("Created by", "Erstellt von"),
"about.organization": ("Organization", "Organisation"),
"about.contact": ("Contact", "Kontakt"),
"about.website": ("Website", "Webseite"),
"about.repository": ("Repository", "Repository"),
"about.license": ("License", "Lizenz"),
"about.copyright": ("Copyright", "Copyright"),
"ldap.title": ("LDAP users", "LDAP-Benutzer"),
"ldap.description": ("Show active users from Active Directory and create them in AssetManager.", "Aktive Benutzer aus dem Active Directory anzeigen und im AssetManager anlegen."),
"ldap.back_to_users": ("Back to user management", "Zur Benutzerverwaltung"),
"ldap.search_label": ("Username, display name or email", "Benutzername, Anzeigename oder E-Mail"),
"ldap.search_placeholder": ("Empty = all, wildcard: *", "Leer = alle, Wildcard: *"),
"ldap.query": ("Search", "Abfragen"),
"ldap.show_all": ("Show all AD users", "Alle AD-Benutzer anzeigen"),
"ldap.search_help": ("An empty search shows all active users. Examples: schmidt*, *verwaltung*. Disabled AD accounts are excluded.", "Eine leere Eingabe zeigt alle aktiven Benutzer. Beispiele: schmidt*, *verwaltung*. Deaktivierte AD-Konten werden ausgeblendet."),
"ldap.users_found": ("{count} users found", "{count} Benutzer gefunden"),
"ldap.save_selected": ("Save selected users", "Ausgewählte Benutzer speichern"),
"ldap.username": ("Username", "Benutzername"),
"ldap.display_name": ("Display name", "Anzeigename"),
"ldap.email": ("Email", "E-Mail"),
"ldap.status": ("Status", "Status"),
"ldap.dn": ("Distinguished Name", "Distinguished Name"),
"ldap.can_save": ("Can be saved", "Kann gespeichert werden"),
"ldap.exists": ("Already exists", "Bereits vorhanden"),

}

BASE_TRANSLATIONS.update({
    "login.title": ("Sign in", "Anmelden"),
    "login.username": ("Username", "Benutzername"),
    "login.password": ("Password", "Passwort"),
    "login.current_password": ("Current password", "Aktuelles Passwort"),
    "login.submit": ("Sign in", "Anmelden"),
    "translations.add": ("Add translation", "Übersetzung hinzufügen"),
    "translations.delete_selected": ("Delete selected", "Markierte löschen"),
    "translations.delete_confirm": ("Really delete the selected custom translations?", "Die ausgewählten eigenen Übersetzungen wirklich löschen?"),
    "translations.select_all": ("Select all custom translations", "Alle eigenen Übersetzungen markieren"),
    "translations.builtin": ("Built-in", "Systemeintrag"),
    "translations.builtin_protected": ("Built-in translation keys cannot be deleted.", "System-Übersetzungsschlüssel können nicht gelöscht werden."),

    "profile.toast_settings": ("Toast notifications", "Toast-Meldungen"),
    "profile.toast_position": ("Position", "Position"),
    "profile.toast_duration": ("Display duration in seconds", "Anzeigedauer in Sekunden"),
    "profile.toast_top_left": ("Top left", "Links oben"),
    "profile.toast_top_center": ("Top center", "Mitte oben"),
    "profile.toast_top_right": ("Top right", "Rechts oben"),
    "profile.toast_bottom_left": ("Bottom left", "Links unten"),
    "profile.toast_bottom_center": ("Bottom center", "Mitte unten"),
    "profile.toast_bottom_right": ("Bottom right", "Rechts unten"),
    "common.duplicate": ("Duplicate", "Duplizieren"),
    "common.edit": ("Edit", "Bearbeiten"),
    "common.delete": ("Delete", "Löschen"),
    "common.description": ("Description", "Beschreibung"),
    "common.field": ("Field", "Feld"),
    "common.unknown": ("unknown", "unbekannt"),
    "assets.return": ("Return", "Zurücknehmen"),
    "assets.history": ("History", "Historie"),
    "assets.child_assets": ("Child assets", "Untergeordnete Geräte"),
    "mesh.debug": ("Mesh debug", "Mesh-Debug"),
    "mesh.debug_start": ("Read device details", "Gerätedetails auslesen"),
    "mesh.debug_loading": ("Reading MeshCentral details…", "MeshCentral-Details werden gelesen…"),
    "mesh.debug_failed": ("Mesh debug failed", "Mesh-Debug fehlgeschlagen"),
    "mesh.debug_success": ("MeshCentral details were written to the browser console and the server debug log. Open F12 → Console.", "Die MeshCentral-Details wurden in die Browser-Konsole und das Server-Debugprotokoll geschrieben. Öffne F12 → Konsole."),
    "mesh.debug_reauth_help": ("For security, enter your current password again before reading the raw MeshCentral device data.", "Gib aus Sicherheitsgründen dein aktuelles Passwort erneut ein, bevor die rohen MeshCentral-Gerätedaten gelesen werden."),
    "mesh.node_id": ("Node ID", "Node-ID"),
    "mesh.last_sync": ("Last synchronization", "Letzte Synchronisierung"),
    "mesh.field_conflicts": ("Field conflicts", "Feldkonflikte"),
    "mesh.local_value": ("Local", "Lokal"),
    "mesh.rule": ("Rule", "Regel"),
    "categories.edit": ("Edit category", "Kategorie bearbeiten"),
    "categories.image": ("Category image", "Kategoriebild"),
    "categories.fields_title": ("Fields of the central asset table", "Felder der zentralen Asset-Tabelle"),
    "categories.database_field": ("Database field", "Datenbankfeld"),
    "categories.display_name": ("Display name", "Anzeigename"),
    "categories.required": ("Required", "Pflicht"),
    "categories.in_list": ("In list", "In Liste"),
    "categories.readonly": ("Read only", "Nur Lesen"),
})


BASE_TRANSLATIONS.update({
    "inventory.title": ("Device inventory", "Geräteinventar"),
    "inventory.subtitle": ("Hardware and software data from the latest MeshCentral synchronization.", "Hardware- und Softwaredaten aus der letzten MeshCentral-Synchronisierung."),
    "inventory.download_json": ("Download JSON", "JSON herunterladen"),
    "inventory.snapshot": ("Inventory snapshot", "Inventarstand"),
    "inventory.tab.hardware": ("Hardware", "Hardware"),
    "inventory.tab.software": ("Software", "Software"),
    "inventory.tab.raw_json": ("MeshCentral JSON", "MeshCentral JSON"),
    "inventory.tab.sync": ("Synchronization", "Synchronisierung"),
    "inventory.no_hardware": ("No hardware details have been stored for this asset yet. Run a MeshCentral synchronization.", "Für dieses Asset wurden noch keine Hardwaredetails gespeichert. Bitte eine MeshCentral-Synchronisierung ausführen."),
    "inventory.software_filter": ("Filter software", "Software filtern"),
    "inventory.software_filter_placeholder": ("Name, version or publisher", "Name, Version oder Hersteller"),
    "inventory.no_software_title": ("No software list available.", "Keine Softwareliste vorhanden."),
    "inventory.no_software_help": ("The MeshCentral device details currently contain no installed software for this device. This area is populated automatically as soon as the source provides a software segment.", "Die aktuell von MeshCentral gelieferte Geräte-Detailabfrage enthält bei diesem Gerät keine installierte Software. Der Bereich wird automatisch gefüllt, sobald die Quelle ein Softwaresegment liefert."),
    "inventory.no_raw_data": ("No MeshCentral raw data stored.", "Keine MeshCentral-Rohdaten gespeichert."),
    "inventory.entry": ("Entry", "Eintrag"),
    "inventory.value.unknown": ("Unknown", "Unbekannt"),
    "inventory.section.system_overview": ("System overview", "Systemübersicht"),
    "inventory.section.bios": ("BIOS / UEFI", "BIOS / UEFI"),
    "inventory.section.mainboard": ("Mainboard", "Mainboard"),
    "inventory.section.processors": ("Processors", "Prozessoren"),
    "inventory.section.memory": ("Memory", "Arbeitsspeicher"),
    "inventory.section.storage": ("Storage and volumes", "Datenträger und Volumes"),
    "inventory.section.graphics": ("Graphics adapters", "Grafikkarten"),
    "inventory.section.network": ("Network", "Netzwerk"),
    "inventory.section.tpm": ("TPM and security hardware", "TPM und Sicherheitshardware"),
    "inventory.section.os_details": ("Operating system details", "Betriebssystemdetails"),
    "inventory.section.security": ("Security", "Sicherheit"),
    "inventory.section.mesh_runtime": ("MeshCentral and runtime", "MeshCentral und Laufzeit"),
    "inventory.section.other_hardware": ("Other hardware data", "Weitere Hardwaredaten"),
    "inventory.section.other_windows_hardware": ("Other Windows hardware data", "Weitere Windows-Hardwaredaten"),
    "inventory.group.physical_drives": ("Physical drives", "Physische Datenträger"),
    "inventory.group.device_identifiers": ("Device identifiers", "Gerätekennungen"),
    "inventory.group.partitions": ("Partitions", "Partitionen"),
    "inventory.group.volumes": ("Volumes", "Volumes"),
    "inventory.group.adapters": ("Adapters", "Adapter"),
    "inventory.group.dns_servers": ("DNS servers", "DNS-Server"),
    "inventory.group.antivirus": ("Antivirus", "Antivirus"),
    "inventory.group.windows_security_center": ("Windows Security Center", "Windows-Sicherheitscenter"),
    "inventory.group.microsoft_defender": ("Microsoft Defender", "Microsoft Defender"),
    "inventory.group.mesh_agent": ("MeshAgent", "MeshAgent"),
    "inventory.group.agent_build": ("Agent build", "Agent-Build"),
    "inventory.group.intel_amt": ("Intel AMT", "Intel AMT"),
    "inventory.group.known_users": ("Known users", "Bekannte Benutzer"),
    "inventory.group.logged_on_users": ("Logged-on users", "Angemeldete Benutzer"),
    "inventory.group.last_connection": ("Last connection", "Letzte Verbindung"),
    "inventory.property.name": ("Name", "Name"),
    "inventory.property.status": ("Status", "Status"),
    "inventory.property.type": ("Type", "Typ"),
    "inventory.property.mac_address": ("MAC address", "MAC-Adresse"),
    "inventory.property.ipv4_address": ("IPv4 address", "IPv4-Adresse"),
    "inventory.property.ipv6_address": ("IPv6 address", "IPv6-Adresse"),
    "inventory.property.gateway": ("Gateway", "Gateway"),
    "inventory.property.netmask": ("Netmask", "Netzmaske"),
    "inventory.property.dns_suffix": ("DNS suffix / FQDN", "DNS-Suffix/FQDN"),
    "inventory.property.interface_index": ("Interface index", "Schnittstellenindex"),
    "inventory.property.enabled": ("Enabled", "Aktiv"),
    "inventory.property.up_to_date": ("Up to date", "Aktuell"),
    "inventory.property.device_name": ("Device name", "Gerätename"),
    "inventory.property.manufacturer": ("Manufacturer", "Hersteller"),
    "inventory.property.model": ("Model", "Modell"),
    "inventory.property.product_uuid": ("Product UUID", "Produkt-UUID"),
    "inventory.property.serial_number": ("Serial number", "Seriennummer"),
    "inventory.property.mesh_group": ("MeshCentral group", "MeshCentral-Gruppe"),
    "inventory.property.operating_system": ("Operating system", "Betriebssystem"),
    "inventory.property.version": ("Version", "Version"),
    "inventory.property.date": ("Date", "Datum"),
    "inventory.property.mode": ("Mode", "Modus"),
    "inventory.property.last_boot_unix_ms": ("Last system boot (Unix ms)", "Letzter Systemstart (Unix ms)"),
})


BASE_TRANSLATIONS.update({
    "common.none": ("none", "keine"),
    "settings.fields": ("Fields", "Felder"),
    "settings.current_logo": ("Current company logo", "Aktuelles Firmenlogo"),
    "settings.custom_css": ("Custom CSS", "Eigenes CSS"),
    "settings.custom_css_help": ("This CSS is loaded after the standard app.css file and can override its appearance. Changes apply to all users and also to the sign-in page.", "Dieser CSS-Code wird nach der Standarddatei app.css geladen und kann deren Darstellung überschreiben. Änderungen gelten für alle Benutzer und bereits auf der Anmeldeseite."),
    "settings.fallback_category": ("Fallback category", "Fallback-Kategorie"),
    "settings.use_fallback": ("Use fallback", "Fallback verwenden"),
    "settings.auth_help": ("Administrators have full access. Non-administrators have read-only access to assigned MeshCentral groups.", "Administratoren haben Vollzugriff. Nicht-Administratoren erhalten ausschließlich Lesezugriff auf zugewiesene MeshCentral-Gruppen."),
    "settings.auth_mode": ("Sign-in mode", "Anmeldemodus"),
    "settings.auth_none": ("No sign-in", "Keine Anmeldung"),
    "settings.auth_local": ("Local users", "Lokale Benutzer"),
    "settings.ldap_connection": ("LDAP connection", "LDAP-Verbindung"),
    "charts.type": ("Chart type", "Diagrammtyp"),
    "charts.grouping": ("Grouping", "Gruppierung"),
    "charts.status_filter": ("Status filter", "Statusfilter"),
    "charts.dashboard": ("Dashboard", "Dashboard"),
    "charts.system_chart": ("Standard chart", "Standarddiagramm"),
    "charts.excluded": ("Excluded", "Ausgeschlossen"),
    "charts.delete_confirm": ("Really delete this chart?", "Diagramm wirklich löschen?"),
    "charts.edit": ("Edit chart", "Diagramm bearbeiten"),
    "charts.system_help": ("This is a standard chart. It can be fully viewed and edited, but not deleted.", "Dies ist ein Standarddiagramm. Es kann vollständig eingesehen und bearbeitet, aber nicht gelöscht werden."),
    "charts.group_by": ("Group by", "Gruppieren nach"),
    "charts.stack_field": ("Subdivision for stacked chart", "Unterteilung bei gestapeltem Diagramm"),
    "charts.sorting": ("Sorting", "Sortierung"),
    "charts.count_desc": ("Count descending", "Anzahl absteigend"),
    "charts.alphabetical": ("Alphabetical", "Alphabetisch"),
    "charts.max_items": ("Maximum items", "Maximale Einträge"),
    "charts.value_display": ("Value display", "Wertedarstellung"),
    "charts.raw_values": ("Raw values", "Rohwerte"),
    "charts.auto_group": ("Group automatically", "Automatisch zusammenfassen"),
    "charts.custom_mappings": ("Custom mappings", "Eigene Zuordnungen"),
    "charts.show_dashboard": ("Show on dashboard", "Auf dem Dashboard anzeigen"),
    "charts.include_empty": ("Include empty values", "Leere Werte berücksichtigen"),
    "charts.status_filter_help": ("Selected status values are excluded from this chart. Multiple status values can be selected.", "Markierte Statuswerte werden bei der Berechnung dieses Diagramms nicht mitgezählt. Mehrere Statuswerte können gleichzeitig ausgeschlossen werden."),
    "charts.no_status_values": ("No status values have been created.", "Keine Statuswerte angelegt."),
    "charts.custom_value_mappings": ("Custom value mappings", "Eigene Wertzuordnungen"),
    "charts.mapping_help": ("One rule per line in the format: search text => display name", "Eine Regel je Zeile im Format: Suchtext => Anzeigename"),
})


BASE_TRANSLATIONS.update({
    "settings.mesh_type_desktop": ("Desktop / PC", "Desktop / PC"),
    "settings.mesh_type_notebook": ("Notebook / laptop", "Notebook / Laptop"),
    "settings.mesh_type_mobile": ("Tablet / mobile device", "Tablet / Mobilgerät"),
    "settings.mesh_type_server": ("Server / appliance", "Server / Appliance"),
    "settings.mesh_type_5": ("Embedded / appliance", "Embedded / Appliance"),
    "settings.mesh_type_6": ("Network device / appliance", "Netzwerkgerät / Appliance"),
    "settings.mesh_type_7": ("Raspberry Pi", "Raspberry Pi"),
    "settings.mesh_type_linux_other": ("Display / terminal", "Display / Terminal"),
    "settings.meshcentral_type": ("MeshCentral icon", "MeshCentral-Icon"),
    "settings.ldap_server": ("LDAP server", "LDAP-Server"),
    "settings.port": ("Port", "Port"),
    "settings.use_ssl": ("Use SSL/LDAPS", "SSL/LDAPS verwenden"),
    "settings.use_starttls": ("Use StartTLS", "StartTLS verwenden"),
    "settings.bind_user": ("Bind DN / bind user", "Bind-DN / Bind-Benutzer"),
    "settings.bind_password_env": ("Environment variable for bind password", "Umgebungsvariable für Bind-Passwort"),
    "settings.user_base_dn": ("User base DN", "Benutzer-Basis-DN"),
    "settings.user_filter": ("User filter", "Benutzerfilter"),
    "settings.display_name_attribute": ("Display name attribute", "Attribut Anzeigename"),
    "settings.email_attribute": ("Email attribute", "Attribut E-Mail"),
    "settings.bind_password_help": ("The bind password remains outside the config, for example in .env as LDAP_BIND_PASSWORD=...", "Das Bind-Passwort bleibt außerhalb der Config, z. B. in .env als LDAP_BIND_PASSWORD=..."),
    "settings.mesh_icon_mapping_help": ("The MeshCentral device icon (node.icon/platformType) is used for category selection when a new asset is created. Existing assets are not recategorized automatically.", "Für die Kategorieauswahl bei neu angelegten Assets wird das MeshCentral-Geräte-Icon (node.icon/platformType) verwendet. Bestehende Assets werden nicht automatisch umkategorisiert."),
    "assets.device_visual": ("Device", "Gerät"),
    "assets.current_image": ("Current asset image", "Aktuelles Assetbild"),
    "assets.remove_image": ("Remove asset image", "Assetbild entfernen"),
    "assets.image_remove_pending": ("The asset image will be removed when you save.", "Assetbild wird beim Speichern entfernt."),
    "assets.image_replace_help": ("A newly selected image replaces the current asset image.", "Ein neu ausgewähltes Bild ersetzt das vorhandene Assetbild."),
    "assets.visual_source": ("Display source", "Anzeigequelle"),
    "assets.visual_source.auto": ("Automatic: asset image → category image → MeshCentral icon", "Automatisch: Assetbild → Kategoriebild → MeshCentral-Icon"),
    "assets.visual_source.asset_image": ("Asset image only", "Nur Assetbild"),
    "assets.visual_source.category_image": ("Category image", "Kategoriebild"),
    "assets.visual_source.mesh_icon": ("MeshCentral icon", "MeshCentral-Icon"),
    "assets.visual_source.help": ("Selects which image or icon is preferred for this asset in lists and details.", "Legt fest, welches Bild oder Icon für dieses Asset in Listen und Details bevorzugt angezeigt wird."),
    "categories.image_help": ("This image is used as the category icon when an asset does not use its own image.", "Dieses Bild wird als Kategorie-Icon verwendet, wenn ein Asset kein eigenes Bild verwendet."),
    "categories.current_image": ("Current category image", "Aktuelles Kategoriebild"),
    "categories.remove_image": ("Remove category image", "Kategoriebild entfernen"),
    "categories.image_remove_pending": ("The category image will be removed when you save.", "Kategoriebild wird beim Speichern entfernt."),
    "images.direct_upload": ("Upload individual image", "Eigenes Bild hochladen"),
    "images.library": ("Standard image library", "Standardbild-Bibliothek"),
    "images.library_help": ("Select an existing standard image or add an image to the library once.", "Wähle ein bereits vorhandenes Standardbild oder füge ein Bild einmalig zur Bibliothek hinzu."),
    "images.library_empty": ("No standard images available yet.", "Noch keine Standardbilder vorhanden."),
    "images.library_upload": ("Add new standard image and use it", "Neues Standardbild hinzufügen und verwenden"),
    "images.library_upload_help": ("The image is stored once in the shared library and can then be reused for other assets and categories.", "Das Bild wird einmal in der gemeinsamen Bibliothek gespeichert und kann danach bei weiteren Assets und Kategorien wiederverwendet werden."),
    "assets.mesh_icon_title": ("MeshCentral device icon {icon}", "MeshCentral-Geräte-Icon {icon}"),
})


_cache: dict[str, dict[str,str]] = {}

def clear_translation_cache(): _cache.clear()

def seed_i18n(db: Session) -> None:
    for code,name,native,default,order in [("en","English","English",True,0),("de","German","Deutsch",False,1)]:
        lang=db.query(Language).filter(Language.code==code).first()
        if not lang:
            db.add(Language(code=code,name=name,native_name=native,is_active=True,is_default=default,sort_order=order))
    db.flush()
    for key,(en,de) in BASE_TRANSLATIONS.items():
        for code,value in (("en",en),("de",de)):
            row = db.query(Translation).filter(
                Translation.translation_key == key,
                Translation.language_code == code,
            ).first()
            if not row:
                db.add(Translation(translation_key=key,language_code=code,text_value=value))

    # Earlier 0.3.7 test builds could leave German text in the English
    # column for login keys. Because these keys are fixed protected
    # system text, reset them to the canonical values.
    # Custom translation keys remain unchanged.
    for key in (
        "login.title",
        "login.username",
        "login.password",
        "login.submit",
        "software.settings.retry_help",
        "software.settings.retry_enabled",
        "software.settings.retry_enabled_help",
        "software.settings.retry_interval_help",
        "software.settings.retry_statuses_value",
    ):
        en_value, de_value = BASE_TRANSLATIONS[key]
        for code, value in (("en", en_value), ("de", de_value)):
            row = db.query(Translation).filter(
                Translation.translation_key == key,
                Translation.language_code == code,
            ).first()
            if row:
                row.text_value = value

    db.commit()
    clear_translation_cache()

def languages(db: Session, active_only=True):
    q=db.query(Language)
    if active_only: q=q.filter(Language.is_active.is_(True))
    return q.order_by(Language.sort_order,Language.code).all()

def dictionary(db: Session, language_code: str) -> dict[str,str]:
    code=(language_code or "en").lower()
    if code in _cache: return _cache[code]
    data={r.translation_key:r.text_value for r in db.query(Translation).filter(Translation.language_code==code).all()}
    _cache[code]=data
    return data

def translate(db: Session, language_code: str, key: str, default: str|None=None, **values) -> str:
    own=dictionary(db,language_code).get(key)
    english=dictionary(db,"en").get(key)
    text=own or english or default or key
    try: return text.format(**values)
    except (KeyError,ValueError): return text

BASE_TRANSLATIONS.update({
    "common.important": ("Important", "Wichtig"),
    "assets.select_named": ("Select asset {name}", "Asset {name} markieren"),
    "assets.bulk_delete_confirm": ("Really delete the selected assets? This action cannot be undone.", "Die markierten Assets wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden."),
    "assets.total": ("{count} assets", "{count} Assets"),
    "assets.asset_id": ("Asset ID", "Asset-ID"),
    "duplicates.merge": ("Merge duplicates", "Duplikate zusammenführen"),
    "duplicates.search": ("Find duplicates", "Duplikate suchen"),
    "duplicates.search_title": ("Find duplicates in the current list", "Duplikate in der aktuellen Liste suchen"),
    "duplicates.visible_only_help": ("Only records currently visible after table filtering are evaluated.", "Es werden nur die aktuell durch Tabellenfilter sichtbaren Datensätze ausgewertet."),
    "duplicates.ignore_empty": ("Ignore empty values", "Leere Werte ignorieren"),
    "duplicates.ignore_case": ("Ignore letter case", "Groß-/Kleinschreibung ignorieren"),
    "duplicates.clear_filter": ("Clear duplicate filter", "Dublettenfilter aufheben"),
    "duplicates.show": ("Show duplicates", "Duplikate anzeigen"),
    "duplicates.active_filter": ("Duplicates: {field}", "Duplikate: {field}"),
    "duplicates.result_summary": ("{records} records in {groups} duplicate groups found.", "{records} Datensätze in {groups} Dublettengruppen gefunden."),
    "assets.active_selection": ("Active selection", "Aktive Auswahl"),
    "assets.category_filter": ("Category: {category}", "Kategorie: {category}"),
    "assets.clear_category_filter": ("Remove category filter", "Kategoriefilter entfernen"),
    "duplicates.remove_filter": ("Remove duplicate filter", "Dublettenfilter entfernen"),
    "duplicates.order_help": ("The top record remains. Empty fields are then filled in the selected order.", "Der oberste Datensatz bleibt bestehen. Danach werden in der Reihenfolge nur noch leere Felder ergänzt."),
    "duplicates.order_notice": ("The category and name of the top asset remain authoritative. The other assets are deleted only after confirmation.", "Kategorie und Bezeichnung des obersten Assets bleiben maßgeblich. Die übrigen Assets werden erst nach der Bestätigung gelöscht."),
    "duplicates.preview": ("View result", "Ergebnis ansehen"),
    "duplicates.preview_title": ("Merge preview", "Vorschau der Zusammenführung"),
    "duplicates.preview_help": ("Asset {id} remains. Only empty values are filled.", "Asset {id} bleibt bestehen. Nur leere Werte werden ergänzt."),
    "duplicates.result": ("Result", "Ergebnis"), "duplicates.source": ("Source", "Quelle"),
    "duplicates.survives": ("remains", "bleibt bestehen"), "duplicates.will_delete": ("will be deleted", "wird gelöscht"),
    "duplicates.change_order": ("Change order", "Reihenfolge ändern"),
    "duplicates.merge_save": ("Merge and save", "Zusammenführen und speichern"),
    "duplicates.confirm_apply": ("Save the merge permanently? The following assets will be deleted.", "Zusammenführung endgültig speichern? Die nachfolgenden Assets werden gelöscht."),
    "duplicates.merge_unique_error": ("The duplicates could not be merged because a unique value is already assigned to another asset.", "Die Duplikate konnten nicht zusammengeführt werden, weil ein eindeutiger Wert bereits einem anderen Asset zugeordnet ist."),
    "settings.system_information": ("System information", "Systeminformationen"),
    "settings.system_information_file_help": ("These values are read from /app/config/APPINFO.json and VERSION and cannot be edited here.", "Diese Angaben werden aus /app/config/APPINFO.json und VERSION gelesen und können hier nicht bearbeitet werden."),
"settings.storage_information": ("Storage and runtime", "Speicher und Laufzeit"),
"settings.storage_information_help": ("The following paths and states are detected at runtime inside the container.", "Die folgenden Pfade und Zustände werden zur Laufzeit im Container ermittelt."),
"settings.database": ("Database", "Datenbank"),
"settings.database_connected": ("PostgreSQL connected", "PostgreSQL verbunden"),
"settings.config_directory": ("Configuration directory", "Konfigurationsverzeichnis"),
"settings.upload_directory": ("Upload directory", "Uploadverzeichnis"),
"settings.log_directory": ("Log directory", "Logverzeichnis"),
"settings.backup_directory": ("Backup directory", "Backupverzeichnis"),
"settings.backup_writable": ("Backup directory writable", "Backupverzeichnis beschreibbar"),
"settings.backup_mount_detected": ("Dedicated Docker mount detected", "Eigenes Docker-Mount erkannt"),
"settings.latest_backup": ("Latest backup", "Letztes Backup"),
"settings.backup_count": ("Number of available backups", "Anzahl vorhandener Backups"),
"settings.backup_storage_used": ("Backup storage used", "Belegter Backup-Speicher"),
    "settings.appinfo_path": ("APPINFO file", "APPINFO-Datei"),
    "mesh.connection": ("Connection", "Verbindung"), "mesh.password_env_help": ("The password is read only from the specified environment variable and is not stored in config.json.", "Das Kennwort wird ausschließlich aus der angegebenen Umgebungsvariable gelesen und nicht in der config.json gespeichert."),
    "mesh.username": ("Username", "Benutzername"), "mesh.password_env": ("Password environment variable", "Kennwort-Umgebungsvariable"),
    "mesh.timeout_seconds": ("Timeout in seconds", "Zeitlimit in Sekunden"), "mesh.include_details": ("Load device details", "Gerätedetails laden"),
    "mesh.create_missing_assets": ("Create missing assets", "Fehlende Assets anlegen"), "mesh.mark_missing_devices": ("Mark devices no longer found", "Nicht mehr gefundene Geräte markieren"),
    "mesh.store_source_json": ("Store source JSON on asset", "Quell-JSON am Asset speichern"), "mesh.save_connection": ("Save connection and behavior", "Verbindung und Verhalten speichern"),
    "mesh.field_mappings": ("Field mappings", "Feld-Mappings"), "mesh.mappings_help": ("Synchronization uses only the enabled rules in this table, including system and custom fields. A debug line is written for each applied mapping when a value changes. Dot notation reads nested JSON values; [] traverses lists and | separates alternative paths.", "Die Synchronisierung verwendet ausschließlich die aktivierten Regeln dieser Tabelle – auch für System- und eigene Felder. Im Live-Protokoll erscheint bei einer Änderung für jedes angewendete Mapping eine Debug-Zeile. Punktnotation liest verschachtelte JSON-Werte. [] durchläuft Listen; | trennt alternative Pfade."),
    "mesh.mappings_examples": ("Examples", "Beispiele"), "mesh.priority": ("Priority", "Prio"), "mesh.target_field": ("Target field", "Zielfeld"),
    "mesh.transformation": ("Transformation", "Transformation"), "mesh.multi_values": ("Multiple values", "Mehrfachwerte"), "mesh.separator": ("Separator", "Trenner"), "mesh.update_rule": ("Update rule", "Update-Regel"),
    "mesh.system_mapping": ("System mapping", "System-Mapping"), "mesh.custom_mapping": ("Custom rule", "Eigene Regel"),
    "mesh.rule_fill_empty": ("Empty fields only", "Nur leere Felder"), "mesh.rule_mesh_wins": ("MeshCentral wins", "MeshCentral gewinnt"), "mesh.rule_local_wins": ("Local value wins", "Lokaler Wert gewinnt"),
    "mesh.save_mappings": ("Save mapping rules", "Mapping-Regeln speichern"), "mesh.new_mapping": ("New mapping rule", "Neue Mapping-Regel"),
    "mesh.new_mapping_help": ("Create additional rules for existing system or custom fields. Create custom target fields under Fields first.", "Lege hier zusätzliche Regeln für vorhandene Systemfelder oder eigene Felder an. Eigene Zielfelder werden zuvor unter „Felder“ erstellt."),
    "mesh.create_mapping": ("Create mapping rule", "Mapping-Regel anlegen"), "mesh.delete_mapping_confirm": ("Really delete this mapping rule?", "Mapping-Regel wirklich löschen?"),
    "mesh.empty_response": ("empty response", "leere Antwort"),
})


BASE_TRANSLATIONS.update({
    "backup.title": ("Backups", "Sicherungen"),
    "backup.description": ("Create, download, upload and restore complete AssetManager backups.", "Vollständige AssetManager-Sicherungen erstellen, herunterladen, hochladen und wiederherstellen."),
    "backup.create_now": ("Create backup now", "Jetzt sichern"),
    "backup.automatic_title": ("Automatic backup", "Automatische Sicherung"),
    "backup.directory": ("Backup directory", "Sicherungsverzeichnis"),
    "backup.interval": ("Interval", "Intervall"),
    "backup.every_hours": ("Every {hours} hours", "Alle {hours} Stunden"),
    "backup.retention": ("Retention", "Aufbewahrung"),
    "backup.days": ("{days} days", "{days} Tage"),
    "backup.contents_help": ("Each backup contains a PostgreSQL database dump, configuration, APPINFO and uploaded files. Environment variables and the .env file are not included.", "Jede Sicherung enthält einen PostgreSQL-Datenbankdump, die Konfiguration, APPINFO und hochgeladene Dateien. Umgebungsvariablen und die Datei .env sind nicht enthalten."),
    "backup.upload_title": ("Upload backup", "Sicherung hochladen"),
    "backup.upload": ("Upload", "Hochladen"),
    "backup.available": ("Available backups", "Vorhandene Sicherungen"),
    "backup.file": ("File", "Datei"),
    "backup.created": ("Created", "Erstellt"),
    "backup.version": ("Version", "Version"),
    "backup.reason": ("Reason", "Art"),
    "backup.size": ("Size", "Größe"),
    "backup.download": ("Download", "Herunterladen"),
    "backup.restore": ("Restore", "Wiederherstellen"),
    "backup.restore_title": ("Restore backup", "Sicherung wiederherstellen"),
    "backup.restore_warning": ("The current database, configuration and uploads will be replaced. A safety backup is created immediately before restoration.", "Die aktuelle Datenbank, Konfiguration und Uploads werden ersetzt. Unmittelbar vor der Wiederherstellung wird automatisch eine Sicherheitssicherung erstellt."),
    "backup.restore_confirmation": ("Enter RESTORE to confirm", "Zur Bestätigung RESTORE eingeben"),
    "backup.restore_now": ("Restore now", "Jetzt wiederherstellen"),
    "backup.delete_confirm": ("Delete this backup permanently?", "Diese Sicherung endgültig löschen?"),
    "backup.invalid_metadata": ("Metadata could not be read", "Metadaten konnten nicht gelesen werden"),
    "backup.none": ("No backups are available yet.", "Es sind noch keine Sicherungen vorhanden."),
    "backup.reason_manual": ("Manual", "Manuell"),
    "backup.reason_automatic": ("Automatic", "Automatisch"),
    "backup.reason_before_restore": ("Before restore", "Vor Wiederherstellung"),
    "backup.reason_unknown": ("Unknown", "Unbekannt"),
    "backup.automatic_help": ("Backups are created automatically according to the configured interval and deleted after the retention period.", "Sicherungen werden automatisch im eingestellten Intervall erstellt und nach Ablauf der Aufbewahrungszeit gelöscht."),
    "backup.active": ("Active", "Aktiv"),
    "backup.upload_help": ("Upload an AssetManager backup ZIP to make it available for download or restoration.", "Lade eine AssetManager-Sicherungsdatei im ZIP-Format hoch, um sie herunterzuladen oder wiederherzustellen."),
    "backup.available_help": ("Stored backups can be downloaded, restored or deleted here.", "Gespeicherte Sicherungen können hier heruntergeladen, wiederhergestellt oder gelöscht werden."),
    "backup.none_title": ("No backups yet", "Noch keine Sicherungen"),
    "backup.created_success": ("Backup created", "Sicherung erstellt"),
    "backup.deleted_success": ("Backup deleted", "Sicherung gelöscht"),
    "backup.uploaded_success": ("Backup uploaded", "Sicherung hochgeladen"),
    "backup.restored_success": ("Backup restored", "Sicherung wiederhergestellt"),
    "backup.restore_confirmation_missing": ("Restore confirmation is missing", "Bestätigung für die Wiederherstellung fehlt"),
    "assets.image": ("Image", "Bild"),
    "common.actions": ("Actions", "Aktionen"),
    "common.apply": ("Apply", "Übernehmen"),
})

BASE_TRANSLATIONS.update({
    "app.software": ("Software and jobs", "Software und Jobs"),
    "software.title": ("Software control", "Softwaresteuerung"),
    "software.packages": ("Software packages", "Softwarepakete"),
    "software.recent_jobs": ("Recent jobs", "Letzte Jobs"),
    "software.jobs_count": ("Number of jobs", "Anzahl der Jobs"),
    "software.jobs_filter_all_help": ("Column filters search the complete job history. The selected number only limits how many matching rows are displayed.", "Die Spaltenfilter durchsuchen die vollständige Jobhistorie. Die ausgewählte Anzahl begrenzt nur die angezeigten Treffer."),
    "software.package": ("Package", "Paket"),
    "software.package_type": ("Type", "Typ"),
    "software.parameters": ("Parameters", "Parameter"),
    "software.platform": ("Platform", "Plattform"),
    "software.status": ("Status", "Status"),
    "software.details": ("Details", "Details"),
    "software.open": ("Open", "Öffnen"),
    "software.no_jobs": ("No jobs yet.", "Noch keine Jobs."),
    "software.no_packages": ("No software packages available.", "Keine Softwarepakete vorhanden."),
    "software.asset_title": ("Software: {name}", "Software: {name}"),
    "software.start_inventory": ("Start software inventory", "Software-Inventur starten"),
    "software.back_to_asset": ("Back to asset", "Zurück zum Asset"),
    "software.installed": ("Installed software", "Installierte Software"),
    "software.publisher": ("Publisher", "Hersteller"),
    "software.architecture": ("Architecture", "Architektur"),
    "software.install_date": ("Installation date", "Installationsdatum"),
    "software.last_seen": ("Last seen", "Zuletzt gesehen"),
    "software.no_inventory": ("No software inventory is available yet.", "Noch keine Softwareinventur vorhanden."),
    "software.jobs": ("Jobs", "Jobs"),
    "software.time": ("Time", "Zeit"),
    "software.message": ("Message", "Meldung"),
    "software.log": ("Log", "Protokoll"),
    "software.show": ("Show", "Anzeigen"),
    "software.job_title": ("Software job {id}", "Softwareauftrag {id}"),
    "software.created": ("Created", "Erstellt"),
    "software.sent": ("Sent", "Gesendet"),
    "software.finished": ("Finished", "Beendet"),
    "software.exit_code": ("Exit code", "Exitcode"),
    "software.diagnostics": ("Diagnostics", "Diagnose"),
    "software.local_log": ("Local log file", "Lokale Logdatei"),
    "software.callback_health": ("Callback endpoint test", "Callback-Endpunkt-Test"),
    "software.callback_token_warning": ("The command below contains the time-limited callback token. Run it only on the associated test device and do not share it.", "Der folgende Befehl enthält den zeitlich begrenzten Callback-Token. Nur auf dem zugehörigen Testgerät ausführen und nicht weitergeben."),
    "software.manual_command": ("Manual test command", "Manueller Testbefehl"),
    "software.copy_command": ("Copy command", "Befehl kopieren"),
    "software.client_log": ("Client log", "Client-Protokoll"),
    "software.no_callback": ("No callback has been received yet.", "Noch keine Callback-Rückmeldung."),
    "software.meshctrl": ("MeshCtrl", "MeshCtrl"),
    "software.stdout": ("Standard output", "Standardausgabe"),
    "software.stderr": ("Error output", "Fehlerausgabe"),
    "software.inventory_queued": ("Software inventory was submitted to MeshCentral.", "Software-Inventur wurde an MeshCentral übergeben."),
    "software.status.created": ("Created", "Erstellt"),
    "software.status.sending": ("Sending", "Wird gesendet"),
    "software.status.sent": ("Sent; callback pending", "Gesendet; Callback ausstehend"),
    "software.status.running": ("Running", "Läuft"),
    "software.status.success": ("Successful", "Erfolgreich"),
    "software.status.failed": ("Failed", "Fehler"),
    "software.status.partial": ("Partially successful", "Teilweise erfolgreich"),
    "software.status.timeout": ("Timed out", "Zeitüberschreitung"),
    "software.status.cancelled": ("Cancelled", "Abgebrochen"),
    "backup.version_unknown": ("Not determinable for this older backup", "Bei dieser älteren Sicherung nicht ermittelbar"),
})

BASE_TRANSLATIONS.update({
    "software.settings.menu": ("Software", "Software"),
    "software.settings.title": ("Software settings", "Software-Einstellungen"),
    "software.settings.description": ("Configure job dispatch, callbacks, timeouts and diagnostics without changing the existing MeshCentral configuration.", "Konfiguriert Job-Versand, Callbacks, Zeitlimits und Diagnose, ohne die bestehende MeshCentral-Konfiguration zu verändern."),
    "software.settings.back_to_software": ("Back to software", "Zur Softwareübersicht"),
    "software.settings.dispatch_title": ("Job dispatch", "Job-Versand"),
    "software.settings.dispatch_help": ("Controls how quickly jobs are handed to MeshCentral during bulk operations.", "Steuert, wie schnell Jobs bei Sammelaktionen an MeshCentral übergeben werden."),
    "software.settings.dispatch_badge": ("Active queue control", "Aktive Warteschlangensteuerung"),
    "software.settings.dispatch_delay": ("Pause between job starts in seconds", "Pause zwischen Jobstarts in Sekunden"),
    "software.settings.dispatch_delay_help": ("The first job starts immediately. Each following job waits this interval. Use 0 to disable the pause.", "Der erste Job startet sofort. Jeder weitere Job wartet dieses Intervall. Mit 0 wird die Pause deaktiviert."),
    "software.settings.timeout_check": ("Automatic timeout check", "Automatische Zeitüberschreitungsprüfung"),
    "software.settings.timeout_check_value": ("Every 30 seconds", "Alle 30 Sekunden"),
    "software.settings.retry_title": ("Automatic retries", "Automatische Wiederholungen"),
    "software.settings.retry_help": ("Automatically queues recent incomplete or failed jobs again. The normal dispatch interval continues to apply.", "Reiht aktuelle unvollständige oder fehlgeschlagene Jobs automatisch erneut ein. Das normale Warteintervall des Job-Versands gilt weiterhin."),
    "software.settings.retry_badge": ("Automatic recovery", "Automatische Wiederherstellung"),
    "software.settings.retry_enabled": ("Automatically restart incomplete or failed jobs", "Unvollständige oder fehlgeschlagene Jobs automatisch erneut starten"),
    "software.settings.retry_enabled_help": ("Sent jobs waiting for a callback, failed, partially successful and timed-out jobs are eligible. Successful, currently sending, running and cancelled jobs are never restarted automatically.", "Berücksichtigt werden gesendete Jobs mit ausstehendem Callback, fehlerhafte, teilweise erfolgreiche und abgelaufene Jobs. Erfolgreiche, aktuell versendete, laufende und abgebrochene Jobs werden niemals automatisch neu gestartet."),
    "software.settings.retry_max_age": ("Maximum job age in hours", "Maximales Jobalter in Stunden"),
    "software.settings.retry_max_age_help": ("Jobs older than this value are ignored. The default is 12 hours.", "Ältere Jobs werden ignoriert. Der Standardwert beträgt 12 Stunden."),
    "software.settings.retry_interval": ("Delay before another retry in seconds", "Wartezeit vor einer weiteren Ausführung in Sekunden"),
    "software.settings.retry_interval_help": ("An eligible status must remain unchanged for at least this period before the job is queued again. For sent jobs, the interval starts at the dispatch time.", "Ein berücksichtigter Status muss mindestens für diese Dauer unverändert bestehen, bevor der Job erneut eingereiht wird. Bei gesendeten Jobs beginnt die Frist mit dem Versandzeitpunkt."),
    "software.settings.retry_max_retries": ("Maximum automatic retries per job", "Maximale automatische Wiederholungen je Job"),
    "software.settings.retry_max_retries_help": ("Limits automatic retries and prevents endless restart loops. Manual restarts remain possible.", "Begrenzt automatische Wiederholungen und verhindert Endlosschleifen. Manuelle Neustarts bleiben weiterhin möglich."),
    "software.settings.retry_statuses": ("Eligible statuses", "Berücksichtigte Status"),
    "software.settings.retry_statuses_value": ("Sent; callback pending · Failed · Partially successful · Timed out", "Gesendet; Callback ausstehend · Fehler · Teilweise erfolgreich · Zeitüberschreitung"),
    "software.settings.callback_title": ("Callback connection", "Callback-Verbindung"),
    "software.settings.callback_help": ("The client must be able to reach this address directly. HTTP and HTTPS addresses are supported.", "Der Client muss diese Adresse direkt erreichen können. HTTP- und HTTPS-Adressen werden unterstützt."),
    "software.settings.callback_badge": ("Job return channel", "Rückkanal der Jobs"),
    "software.settings.callback_base_url": ("Callback base URL", "Callback-Basisadresse"),
    "software.settings.callback_base_url_help": ("Enter only the base URL without an API path, for example https://assetmanager.example.org. If empty, the address of the current browser request is used.", "Nur die Basisadresse ohne API-Pfad eintragen, zum Beispiel https://assetmanager.example.org. Bei leerem Feld wird die Adresse der aktuellen Browseranfrage verwendet."),
    "software.settings.callback_timeout": ("Callback token validity in minutes", "Gültigkeit des Callback-Tokens in Minuten"),
    "software.settings.callback_timeout_help": ("After this period the job no longer accepts a callback.", "Nach diesem Zeitraum nimmt der Job keinen Callback mehr an."),
    "software.settings.callback_workers": ("Parallel callback workers", "Parallele Callback-Worker"),
    "software.settings.callback_workers_help": ("Controls how many callback database imports can run at the same time. A container restart is required after changing this value. Recommended: 4.", "Legt fest, wie viele Callback-Datenbankimporte gleichzeitig laufen dürfen. Nach einer Änderung ist ein Container-Neustart erforderlich. Empfehlung: 4."),
    "software.settings.test_timeout": ("Connection test timeout in seconds", "Zeitlimit des Verbindungstests in Sekunden"),
    "software.settings.test_timeout_help": ("Maximum waiting time for DNS, connection and response.", "Maximale Wartezeit für DNS, Verbindung und Antwort."),
    "software.settings.verify_tls": ("Verify TLS certificate during the test", "TLS-Zertifikat beim Test prüfen"),
    "software.settings.verify_tls_help": ("Disable only for a temporary test with an internal or self-signed HTTPS certificate.", "Nur für einen vorübergehenden Test mit internem oder selbstsigniertem HTTPS-Zertifikat deaktivieren."),
    "software.settings.health_url": ("Resulting health URL", "Ermittelte Testadresse"),
    "software.settings.automatic_url": ("Automatic: current request address", "Automatisch: Adresse der aktuellen Anfrage"),
    "software.settings.debug_title": ("Debug logging", "Debugprotokoll"),
    "software.settings.debug_help": ("Records connection tests and received callbacks in a separate log.", "Zeichnet Verbindungstests und empfangene Callbacks in einem eigenen Protokoll auf."),
    "software.settings.debug_logging": ("Record callback events", "Callback-Ereignisse protokollieren"),
    "software.settings.debug_logging_help": ("Tokens and software contents are not written to this diagnostic log.", "Tokens und Softwareinhalte werden nicht in dieses Diagnoseprotokoll geschrieben."),
    "software.settings.debug_max_lines": ("Maximum log lines", "Maximale Protokollzeilen"),
    "software.settings.debug_max_lines_help": ("Older entries are automatically removed.", "Ältere Einträge werden automatisch entfernt."),
    "software.settings.debug_log_path": ("Log file", "Protokolldatei"),
    "software.settings.test_title": ("Callback test and live debug log", "Callback-Test und Live-Debugprotokoll"),
    "software.settings.test_help": ("The AssetManager calls the configured health endpoint and records DNS resolution, connection, HTTP status and response.", "Der AssetManager ruft den konfigurierten Health-Endpunkt auf und protokolliert DNS-Auflösung, Verbindung, HTTP-Status und Antwort."),
    "software.settings.run_test": ("Test callback", "Callback testen"),
    "software.settings.run_internal_test": ("Test internal health check", "Internen Health-Check testen"),
    "software.settings.run_external_test": ("Test public health check", "Öffentlichen Health-Check testen"),
    "software.settings.internal_health_url": ("Internal callback health URL", "Interne Callback-Health-URL"),
    "software.settings.test_result": ("Health-check result", "Health-Check-Ergebnis"),
    "software.settings.test_reachable": ("Reachable", "Erreichbar"),
    "software.settings.test_unreachable": ("Not reachable", "Nicht erreichbar"),
    "software.settings.test_kind_internal": ("Internal callback service", "Interner Callback-Dienst"),
    "software.settings.test_kind_external": ("Configured/public callback address", "Konfigurierte/öffentliche Callback-Adresse"),
    "software.settings.response_time": ("Response time", "Antwortzeit"),
    "software.settings.log_auto_refresh": ("The log is refreshed automatically every three seconds.", "Das Protokoll wird automatisch alle drei Sekunden aktualisiert."),
    "software.settings.clear_log": ("Clear log", "Protokoll leeren"),
    "software.settings.no_log": ("No diagnostic entries yet.", "Noch keine Diagnoseeinträge vorhanden."),
    "software.settings.future_title": ("Planned extension areas", "Vorgesehene Erweiterungsbereiche"),
    "software.settings.future_help": ("This page is prepared for the following settings as software management grows. These options are not active yet.", "Diese Seite ist für folgende Einstellungen vorbereitet, wenn die Softwareverwaltung wächst. Die Optionen sind noch nicht aktiv."),
    "software.settings.future_queue": ("Queue and concurrency", "Warteschlange und Parallelität"),
    "software.settings.future_retries": ("Retries and error handling", "Wiederholungen und Fehlerbehandlung"),
    "software.settings.future_inventory": ("Inventory sources", "Inventurquellen"),
    "software.settings.future_retention": ("History and retention", "Historie und Aufbewahrung"),
    "software.settings.future_packages": ("Package sources and repositories", "Paketquellen und Repositorys"),
    "software.settings.future_security": ("Execution security and approvals", "Ausführungssicherheit und Freigaben"),
    "software.settings.saved": ("Software settings saved.", "Software-Einstellungen gespeichert."),
    "software.settings.invalid_url": ("Enter a valid HTTP or HTTPS base URL.", "Bitte eine gültige HTTP- oder HTTPS-Basisadresse eingeben."),
    "software.settings.test_success": ("Callback endpoint is reachable.", "Callback-Endpunkt ist erreichbar."),
    "software.settings.test_failed": ("Callback test failed: {error}", "Callback-Test fehlgeschlagen: {error}"),
    "software.settings.log_cleared": ("Debug log cleared.", "Debugprotokoll geleert.")
})

BASE_TRANSLATIONS.update({
    "software.job_id": ("Job ID", "Job-ID"),
    "software.copy_log": ("Copy log", "Log kopieren")
})

BASE_TRANSLATIONS.update({
    "software.initialize_inventory": ("Initialize software list", "Softwareliste initialisieren"),
    "software.update_inventory": ("Update software inventory", "Softwareinventur aktualisieren"),
    "software.inventory_count": ("{count} software entries stored.", "{count} Softwareeinträge gespeichert."),
    "software.inventory_not_initialized": ("Software list not initialized yet", "Softwareliste noch nicht initialisiert"),
    "software.inventory_initialize_help": ("Start the initial inventory using the button above. All detected software is currently included.", "Die Erstinventur über die Schaltfläche oben starten. Aktuell wird sämtliche erkannte Software übernommen."),
    "software.copied": ("Copied", "Kopiert"),
    "software.copy_failed": ("Copy failed", "Kopieren fehlgeschlagen"),
    "software.settings.exclusions_title": ("Inventory exclusion list", "Ausschlussliste für die Inventur"),
    "software.settings.exclusions_help": ("Software whose name contains a listed text is not stored during the next inventory for the selected platform.", "Software, deren Name einen eingetragenen Text enthält, wird bei der nächsten Inventur für die gewählte Plattform nicht gespeichert."),
    "software.settings.active": ("Active", "Aktiv"),
    "software.settings.exclude_platform": ("Platform", "Plattform"),
    "software.settings.exclude_name_contains": ("Software name contains", "Softwarename enthält"),
    "software.settings.exclude_name_placeholder": ("e.g. Security Update", "z. B. Security Update"),
    "software.settings.exclude_add": ("Add exclusion", "Ausschluss hinzufügen"),
    "software.settings.exclude_remove": ("Remove exclusion", "Ausschluss entfernen"),
    "software.settings.exclude_match_help": ("Matching is case-insensitive and checks whether the entered text occurs anywhere in the software name. Empty rows are ignored.", "Die Prüfung erfolgt ohne Beachtung der Groß-/Kleinschreibung und sucht den Text an beliebiger Stelle im Softwarenamen. Leere Zeilen werden ignoriert."),
    "software.platform.all": ("All platforms", "Alle Plattformen")
})

BASE_TRANSLATIONS.update({
    "software.central_description": ("Central overview of all software inventories and software jobs.", "Zentrale Übersicht aller Softwareinventuren und Softwareaufträge."),
    "software.total_entries": ("Software installations", "Softwareinstallationen"),
    "software.distinct_software": ("Aggregated software entries", "Kumulierte Softwareeinträge"),
    "software.initialized_assets": ("Initialized assets", "Initialisierte Assets"),
    "software.all_installations": ("All software installations", "Alle Softwareinstallationen"),
    "software.all_installations_help": ("One row per software entry and asset. Every column can be filtered and sorted.", "Eine Zeile je Softwareeintrag und Asset. Jede Spalte kann gefiltert und sortiert werden."),
    "software.aggregated_list": ("Aggregated software list", "Kumulierte Softwareliste"),
    "software.aggregated_list_help": ("Groups identical software names and publishers and shows their distribution across assets.", "Fasst gleiche Softwarenamen und Hersteller zusammen und zeigt deren Verteilung über die Assets."),
    "software.versions": ("Versions", "Versionen"),
    "software.version_count": ("Number of versions", "Anzahl Versionen"),
    "software.asset_count": ("Assets", "Assets"),
    "software.entry_count": ("Entries", "Einträge"),
    "software.unique_asset_count": ("Unique assets", "Eindeutige Assets"),
    "software.unique_asset_count_help": ("Number of different assets on which this grouped software was found. An asset is counted only once, even if multiple versions or matching inventory records exist.", "Anzahl unterschiedlicher Assets, auf denen diese gruppierte Software gefunden wurde. Ein Asset wird nur einmal gezählt, auch wenn mehrere Versionen oder passende Inventareinträge vorhanden sind."),
    "software.inventory_entry_count": ("Inventory entries", "Inventareinträge"),
    "software.inventory_entry_count_help": ("Number of matching software inventory records. This value can be higher than the number of unique assets when multiple versions or records exist on the same asset.", "Anzahl der passenden Software-Inventardatensätze. Dieser Wert kann höher als die Anzahl eindeutiger Assets sein, wenn auf einem Asset mehrere Versionen oder Einträge vorhanden sind."),
    "software.no_global_inventory": ("No central software inventory is available yet.", "Noch keine zentrale Softwareinventur vorhanden."),
    "software.last_job": ("Last job", "Letzter Job"),
    "software.last_inventory": ("Last inventory", "Letzte Inventur")
})

BASE_TRANSLATIONS.update({
    "software.open_detailed_list": ("Open detailed installation list", "Detaillierte Installationsliste öffnen"),
    "software.open_aggregated_list": ("Open aggregated software list", "Kumulierte Softwareliste öffnen"),
    "software.lists": ("Software lists", "Softwarelisten"),
    "software.detailed_list": ("Detailed installations", "Detaillierte Installationen"),
    "software.summary_table": ("Aggregated entries", "Kumulierte Einträge"),
    "software.table_filter_help": ("Use the filter row below the column headings to narrow the list.", "Die Filterzeile unterhalb der Spaltenüberschriften grenzt die Liste ein."),
    "software.numeric_filter_help": ("Numeric columns support operators such as < 2, >= 5, 1..10 or 'between 1 and 10'.", "Numerische Spalten unterstützen Operatoren wie < 2, >= 5, 1..10 oder „zwischen 1 und 10“."),
    "software.numeric_filter_placeholder": ("e.g. < 2 or 1..5", "z. B. < 2 oder 1..5")
})


BASE_TRANSLATIONS.update({
    "software.grouping": ("Grouping", "Gruppierung"),
    "software.grouping.name_platform": ("Name and platform", "Name und Plattform"),
    "software.grouping.software": ("Name, publisher and platform", "Name, Hersteller und Plattform"),
    "software.grouping.version": ("Name, publisher, platform and version", "Name, Hersteller, Plattform und Version"),
    "software.grouping.help": ("Choose whether publishers and versions are combined or shown as separate rows.", "Lege fest, ob Hersteller und Versionen zusammengefasst oder als eigene Zeilen angezeigt werden."),
})

BASE_TRANSLATIONS.update({
    "field.mesh_online": ("Online", "Online"),
    "presence.title": ("Online status", "Online-Status"),
    "presence.online": ("Online", "Online"),
    "presence.offline": ("Offline", "Offline"),
    "presence.unknown": ("Unknown", "Unbekannt"),
    "presence.updated": ("Updated", "Aktualisiert"),
    "presence.last_online": ("Last online", "Zuletzt online"),
    "presence.last_seen": ("Last seen", "Zuletzt gesehen"),
    "presence.live_help": ("This status is refreshed automatically for assets with a MeshCentral node ID.", "Dieser Status wird für Assets mit MeshCentral Node-ID automatisch aktualisiert.")
})

BASE_TRANSLATIONS.update({
    "jobs.action": ("Action", "Aktion"),
    "jobs.action.run": ("Run", "Ausführen"),
    "jobs.action.install": ("Install", "Installieren"),
    "jobs.action.uninstall": ("Uninstall", "Deinstallieren"),
    "jobs.action.update": ("Update", "Aktualisieren"),
    "jobs.software_inventory": ("Start software inventory", "Softwareinventur starten"),
    "jobs.software_inventory_confirm": ("Create one software inventory job for each of the {count} selected assets?", "Für {count} markierte Assets jeweils einen Softwareinventur-Job erstellen?"),
    "jobs.bulk.none_selected": ("No assets were selected.", "Es wurden keine Assets markiert."),
    "jobs.bulk.inventory_result": ("Created {created} software inventory jobs; skipped {skipped} assets.", "{created} Softwareinventur-Jobs wurden erstellt; {skipped} Assets wurden übersprungen."),
    "jobs.bulk.inventory_none_created": ("No software inventory job could be created; skipped {skipped} assets.", "Es konnte kein Softwareinventur-Job erstellt werden; {skipped} Assets wurden übersprungen.")
})

BASE_TRANSLATIONS.update({
    "jobs.type": ("Job type", "Job-Art"),
    "jobs.select_type": ("Select job type …", "Job-Art auswählen …"),
    "jobs.type.software_inventory": ("Software inventory", "Softwareinventur"),
    "jobs.type.job_definition": ("Job definition", "Jobdefinition"),
    "jobs.start": ("Start job", "Job starten"),
    "jobs.bulk.controls_label": ("Jobs for selected assets", "Jobs für markierte Assets"),
    "jobs.bulk.confirm": ("Create and start the job “{job}” for each of the {count} selected assets?", "Für {count} markierte Assets jeweils den Job „{job}“ erstellen und starten?"),
    "jobs.bulk.invalid_type": ("The selected job type is not available.", "Die ausgewählte Job-Art ist nicht verfügbar."),
    "jobs.bulk.result": ("Created {created} jobs; skipped {skipped} assets.", "{created} Jobs wurden erstellt; {skipped} Assets wurden übersprungen."),
    "jobs.bulk.none_created": ("No job could be created; skipped {skipped} assets.", "Es konnte kein Job erstellt werden; {skipped} Assets wurden übersprungen.")
})


BASE_TRANSLATIONS.update({
    "jobs.title": ("Job {id}", "Job {id}"),
    "jobs.not_found": ("Job not found", "Job nicht gefunden"),
    "jobs.priority": ("Priority", "Priorität"),
    "jobs.parameters": ("Parameters", "Parameter"),
    "jobs.result": ("Result", "Ergebnis"),
    "jobs.created_by": ("Created by", "Erstellt von"),
    "jobs.scheduled_at": ("Scheduled for", "Geplant für"),
    "jobs.attempts": ("Attempts", "Versuche"),
    "jobs.max_attempts": ("Maximum attempts", "Maximale Versuche")
})


BASE_TRANSLATIONS.update({
    "jobs.actions": ("Actions", "Aktionen"),
    "jobs.restart": ("Restart job", "Job erneut starten"),
    "jobs.restart.confirm": ("Restart this existing job? No new job will be created.", "Diesen bestehenden Job erneut starten? Es wird kein neuer Job angelegt."),
    "jobs.restart.started": ("Job was queued again.", "Der Job wurde erneut eingereiht."),
    "jobs.restart.active": ("An active job cannot be restarted.", "Ein aktiver Job kann nicht erneut gestartet werden."),
    "jobs.restart.no_node": ("The asset has no MeshCentral node ID.", "Das Asset besitzt keine MeshCentral Node-ID."),
    "jobs.restart_selected": ("Restart selected jobs", "Markierte Jobs erneut starten"),
    "jobs.select_all_restartable": ("Select all restartable visible jobs", "Alle erneut startbaren sichtbaren Jobs markieren"),
    "jobs.select_for_restart": ("Select job {id} for restart", "Job {id} zum erneuten Start markieren"),
    "jobs.bulk_restart.confirm": ("Restart {count} selected jobs using the configured dispatch interval?", "{count} markierte Jobs mit dem eingestellten Warteintervall erneut starten?"),
    "jobs.bulk_restart.none_selected": ("No jobs were selected.", "Es wurden keine Jobs markiert."),
    "jobs.bulk_restart.none_eligible": ("None of the selected jobs can be restarted.", "Keiner der markierten Jobs kann erneut gestartet werden."),
    "jobs.bulk_restart.started": ("{count} jobs were queued again; {skipped} were skipped.", "{count} Jobs wurden erneut eingereiht; {skipped} wurden übersprungen."),
    "jobs.history": ("Job history", "Jobverlauf"),
    "jobs.history.empty": ("No history events are available yet.", "Es sind noch keine Verlaufsereignisse vorhanden."),
    "jobs.attempt": ("Attempt", "Versuch"),
    "jobs.event": ("Event", "Ereignis"),
    "jobs.event.created": ("Created", "Erstellt"),
    "jobs.event.restart_requested": ("Restart requested", "Neustart angefordert"),
    "jobs.event.automatic_retry_requested": ("Automatic retry requested", "Automatischer Neustart angefordert"),
    "jobs.event.sending": ("Sending", "Wird gesendet"),
    "jobs.event.meshctrl_result": ("MeshCtrl result", "MeshCtrl-Ergebnis"),
    "jobs.event.callback": ("Callback", "Callback"),
    "jobs.resolved_script": ("Resolved script with real values", "Aufgelöstes Skript mit Echtdaten"),
    "jobs.resolved_script_help": ("This is the complete readable script with the real asset, job and callback values used for this execution. The callback URL contains a temporary secret token.", "Dies ist das vollständige lesbare Skript mit den echten Asset-, Job- und Callback-Werten dieser Ausführung. Die Callback-URL enthält einen zeitlich begrenzten geheimen Token."),
    "jobs.copy_resolved_script": ("Copy resolved script", "Aufgelöstes Skript kopieren"),
    "jobs.meshctrl_command": ("Technical MeshCentral command", "Technischer MeshCentral-Befehl"),
    "jobs.event.error": ("Error", "Fehler")
})

BASE_TRANSLATIONS.update({
    "software.selected_count": ("Selected", "Markiert"),
    "software.delete.selected": ("Delete selected inventory entries", "Markierte Inventareinträge löschen"),
    "software.delete.confirm": ("Delete {count} selected software inventory entries?", "{count} markierte Software-Inventareinträge wirklich löschen?"),
    "software.delete.inventory_only": ("This only deletes stored inventory data. No software will be uninstalled from the device.", "Dadurch werden nur gespeicherte Inventardaten gelöscht. Auf dem Gerät wird keine Software deinstalliert."),
    "software.delete.none_selected": ("No software inventory entries were selected.", "Es wurden keine Software-Inventareinträge ausgewählt."),
    "software.delete.result": ("Deleted {count} software inventory entries.", "{count} Software-Inventareinträge wurden gelöscht."),
    "software.select_all_visible": ("Select all visible software inventory entries", "Alle sichtbaren Software-Inventareinträge markieren"),
    "software.select_entry": ("Select {name}", "{name} markieren"),
    "software.platform.windows": ("Windows", "Windows"),
    "software.platform.linux": ("Linux", "Linux"),
    "software.platform.macos": ("macOS", "macOS"),
    "software.platform.apple": ("Apple", "Apple"),
    "software.platform.android": ("Android", "Android"),
    "software.platform.unknown": ("Unknown", "Unbekannt")
})


BASE_TRANSLATIONS.update({
    "jobdefs.title": ("Job definitions", "Jobdefinitionen"),
    "jobdefs.description": ("Manage reusable script and command definitions.", "Wiederverwendbare Skript- und Befehlsdefinitionen verwalten."),
    "jobdefs.new": ("Create job definition", "Jobdefinition anlegen"),
    "jobdefs.edit": ("Edit job definition", "Jobdefinition bearbeiten"),
    "jobdefs.admin_only": ("Only administrators can view and edit job definitions.", "Nur Administratoren können Jobdefinitionen anzeigen und bearbeiten."),
    "jobdefs.platform": ("Platform", "Plattform"),
    "jobdefs.platform.windows": ("Windows", "Windows"),
    "jobdefs.platform.linux": ("Linux", "Linux"),
    "jobdefs.platform.macos": ("macOS", "macOS"),
    "jobdefs.platform.all": ("All platforms", "Alle Plattformen"),
    "jobdefs.interpreter": ("Interpreter", "Interpreter"),
    "jobdefs.interpreter.powershell": ("PowerShell", "PowerShell"),
    "jobdefs.interpreter.cmd": ("CMD / Batch", "CMD / Batch"),
    "jobdefs.interpreter.bash": ("Bash", "Bash"),
    "jobdefs.interpreter.python": ("Python", "Python"),
    "jobdefs.source": ("Script source", "Skriptquelle"),
    "jobdefs.source.inline": ("Inline script", "Inline-Skript"),
    "jobdefs.source.mounted": ("Mounted file", "Gemountete Datei"),
    "jobdefs.source.unc": ("UNC path", "UNC-Pfad"),
    "jobdefs.path": ("Script path", "Skriptpfad"),
    "jobdefs.path_help": ("Use an absolute container path below /scripts or a UNC path beginning with \\", "Einen absoluten Containerpfad unter /scripts oder einen UNC-Pfad beginnend mit \\ verwenden."),
    "jobdefs.inline_script": ("Inline script", "Inline-Skript"),
    "jobdefs.variables_help": ("Available placeholders are replaced before execution. Use the preview to inspect the generated script.", "Verfügbare Platzhalter werden vor der Ausführung ersetzt. Über die Vorschau kann das erzeugte Skript geprüft werden."),
    "jobdefs.arguments": ("Arguments", "Parameter"),
    "jobdefs.timeout": ("Timeout in seconds", "Zeitlimit in Sekunden"),
    "jobdefs.success_codes": ("Successful exit codes", "Erfolgreiche Rückgabecodes"),
    "jobdefs.revision": ("Revision", "Revision"),
    "jobdefs.system": ("System", "System"),
    "jobdefs.none": ("No job definitions are available.", "Keine Jobdefinitionen vorhanden."),
    "jobdefs.none_filtered": ("No job definitions match the current filter.", "Keine Jobdefinitionen entsprechen dem aktuellen Filter."),
    "jobdefs.status": ("Status", "Status"),
    "jobdefs.status.active": ("Active", "Aktiv"),
    "jobdefs.status.inactive": ("Inactive", "Deaktiviert"),
    "jobdefs.filter.search": ("Search", "Suche"),
    "jobdefs.filter.search_placeholder": ("Name or description", "Name oder Beschreibung"),
    "jobdefs.filter.active_only": ("Show active definitions only", "Nur aktive Jobdefinitionen anzeigen"),
    "jobdefs.filter.apply": ("Apply filter", "Filter anwenden"),
    "jobdefs.filter.reset": ("Reset", "Zurücksetzen"),
    "jobdefs.delete.confirm": ("Permanently delete this unused job definition?", "Diese unbenutzte Jobdefinition dauerhaft löschen?"),
    "jobdefs.delete.system_forbidden": ("System job definitions cannot be deleted.", "System-Jobdefinitionen können nicht gelöscht werden."),
    "jobdefs.delete.referenced": ("This job definition is referenced by existing jobs and cannot be deleted. Deactivate it instead.", "Diese Jobdefinition wird von vorhandenen Jobs referenziert und kann nicht gelöscht werden. Deaktivieren Sie sie stattdessen."),
    "jobdefs.deleted": ("Job definition \"{name}\" was deleted.", "Jobdefinition \"{name}\" wurde gelöscht."),
    "jobdefs.delete.behavior_title": ("Deleting and deactivating", "Löschen und Deaktivieren"),
    "jobdefs.delete.behavior": ("Unused custom job definitions can be deleted. System definitions and definitions already referenced by a job remain protected; deactivate them to hide them from new executions.", "Unbenutzte eigene Jobdefinitionen können gelöscht werden. Systemdefinitionen und bereits von einem Job referenzierte Definitionen bleiben geschützt; deaktivieren Sie diese, damit sie nicht mehr für neue Ausführungen angeboten werden."),
    "jobdefs.script_help.title": ("Variables and helper functions", "Variablen und Hilfsfunktionen"),
    "jobdefs.script_help.common": ("The runtime wrapper is added automatically. Placeholders are replaced before execution and may be used anywhere in your own code.", "Der Laufzeitrahmen wird automatisch ergänzt. Platzhalter werden vor der Ausführung ersetzt und können überall im eigenen Code verwendet werden."),
    "jobdefs.script_help.variables": ("Asset and system variables:", "Asset- und Systemvariablen:"),
    "jobdefs.script_help.parameters": ("Job parameters:", "Jobparameter:"),
    "jobdefs.script_help.powershell": ("Write-JobLog accepts any string. PowerShell variables work normally; use double quotes or an expression to include their current values. Add callback result fields through the $JobResult dictionary.", "Write-JobLog übernimmt jede Zeichenkette. PowerShell-Variablen funktionieren normal; verwenden Sie doppelte Anführungszeichen oder einen Ausdruck, um aktuelle Werte einzufügen. Zusätzliche Callback-Ergebnisse werden über das Wörterbuch $JobResult ergänzt."),
    "jobdefs.script_help.python": ("Use print() for output. Values may be formatted with normal Python expressions. Add callback result fields through the job_result dictionary.", "Verwenden Sie print() für Ausgaben. Werte können mit normalen Python-Ausdrücken formatiert werden. Zusätzliche Callback-Ergebnisse werden über das Wörterbuch job_result ergänzt."),
    "jobdefs.script_help.bash": ("Use echo or printf for output and ordinary shell variables. Asset and job parameters are inserted through placeholders before execution.", "Verwenden Sie echo oder printf für Ausgaben und normale Shell-Variablen. Asset- und Jobparameter werden vor der Ausführung über Platzhalter eingesetzt."),
    "jobdefs.script_help.cmd": ("Use echo for output and normal CMD variables such as %NAME%. Asset and job parameters are inserted through placeholders before execution.", "Verwenden Sie echo für Ausgaben und normale CMD-Variablen wie %NAME%. Asset- und Jobparameter werden vor der Ausführung über Platzhalter eingesetzt."),
    "jobdefs.script_help.wrapper_sources": ("Helper functions and automatic callbacks are available for inline and mounted scripts. UNC scripts are started as external files and therefore do not receive wrapper helper functions.", "Hilfsfunktionen und automatische Callbacks stehen für Inline- und eingebundene Skripte zur Verfügung. UNC-Skripte werden als externe Dateien gestartet und erhalten deshalb keine Hilfsfunktionen des Laufzeitrahmens."),
    "jobdefs.saved": ("Job definition saved.", "Jobdefinition wurde gespeichert."),
    "jobdefs.test": ("Validate and preview", "Prüfen und Vorschau"),
    "jobdefs.test_result": ("Validation result", "Prüfergebnis"),
    "jobdefs.preview": ("Generated script preview", "Vorschau des erzeugten Skripts"),
    "jobdefs.preview_help": ("The preview contains sample values for all placeholders and is not executed.", "Die Vorschau enthält Beispielwerte für alle Platzhalter und wird nicht ausgeführt."),
    "jobdefs.copy_preview": ("Copy preview", "Vorschau kopieren"),
    "jobdefs.copied": ("The generated script was copied to the clipboard.", "Das erzeugte Skript wurde in die Zwischenablage kopiert."),
    "jobdefs.copy_failed": ("The script could not be copied automatically.", "Das Skript konnte nicht automatisch kopiert werden."),
    "jobdefs.generate_wrapper": ("Generate execution framework", "Job-Rahmen erzeugen"),
    "jobdefs.generate_wrapper_confirm": ("Replace the current inline script with a generated framework?", "Das aktuelle Inline-Skript durch einen erzeugten Ausführungsrahmen ersetzen?"),
    "jobdefs.wrapper_help": ("Creates logging, error handling and callback code for the selected interpreter. Add your own commands in the marked section.", "Erzeugt Protokollierung, Fehlerbehandlung und Callback-Code für den ausgewählten Interpreter. Eigene Befehle werden im markierten Abschnitt ergänzt."),
    "jobdefs.validation.ok": ("Validation completed successfully.", "Prüfung erfolgreich abgeschlossen."),
    "jobdefs.validation.inline_empty": ("The inline script is empty.", "Das Inline-Skript ist leer."),
    "jobdefs.validation.mounted_absolute": ("The mounted path must be absolute.", "Der gemountete Pfad muss absolut sein."),
    "jobdefs.validation.file_missing": ("File not found: {path}", "Datei nicht gefunden: {path}"),
    "jobdefs.validation.file_readable": ("File readable: {path} ({count} characters)", "Datei lesbar: {path} ({count} Zeichen)"),
    "jobdefs.validation.unc_prefix": ("A UNC path must begin with \\", "Ein UNC-Pfad muss mit \\ beginnen."),
    "jobdefs.validation.unc_ok": ("UNC syntax is valid. Reachability is checked on the target device.", "Die UNC-Syntax ist gültig. Die Erreichbarkeit wird auf dem Zielgerät geprüft."),
    "jobdefs.validation.python_ok": ("Python syntax is valid.", "Die Python-Syntax ist gültig."),
    "jobdefs.validation.python_error": ("Python syntax error on line {line}: {message}", "Python-Syntaxfehler in Zeile {line}: {message}"),
    "jobdefs.validation.bash_ok": ("Bash syntax is valid.", "Die Bash-Syntax ist gültig."),
    "jobdefs.validation.windows_later": ("Content is present. Full syntax validation is performed later on a Windows test device.", "Inhalt vorhanden. Eine vollständige Syntaxprüfung erfolgt später auf einem Windows-Testgerät."),
    "jobdefs.preview.sample_notice": ("Sample values used for preview", "Für die Vorschau verwendete Beispielwerte"),
    "jobdefs.insert_test_script": ("Insert test script", "Testskript einsetzen"),
    "jobdefs.insert_test_script_confirm": ("Replace the current inline script with a safe test script?", "Das aktuelle Inline-Skript durch ein ungefährliches Testskript ersetzen?"),
    "jobdefs.test_script_help": ("The test script only outputs resolved variables and uses the generated callback framework. It does not change the target device.", "Das Testskript gibt nur ersetzte Variablen aus und verwendet den erzeugten Callback-Rahmen. Es verändert das Zielgerät nicht."),
    "jobdefs.wrapper_generated": ("The job framework was inserted.", "Der Job-Rahmen wurde eingesetzt."),
    "jobdefs.test_script_inserted": ("The safe test script was inserted.", "Das ungefährliche Testskript wurde eingesetzt."),
    "jobdefs.generator_generating": ("Generating script...", "Skript wird erzeugt..."),
    "jobdefs.generator_failed": ("The script could not be generated.", "Das Skript konnte nicht erzeugt werden."),
    "jobdefs.own_script": ("Your script commands", "Eigene Skriptbefehle"),
    "jobdefs.own_script_help": ("Enter only your commands. Logging, error handling and callback are generated automatically at runtime. Parameters can be used as {{Param.key}}.", "Nur die eigenen Befehle eingeben. Protokollierung, Fehlerbehandlung und Callback werden zur Laufzeit automatisch erzeugt. Parameter können als {{Param.schluessel}} verwendet werden."),
    "jobdefs.insert_test_commands": ("Insert test commands", "Testbefehle einsetzen"),
    "jobdefs.test_commands_content": ("Write-Output \"AssetManager test\"\nWrite-Output \"Asset={{AssetName}}\"", "Write-Output \"AssetManager-Test\"\nWrite-Output \"Asset={{AssetName}}\""),
    "jobparams.title": ("Parameters", "Parameter"),
    "jobparams.help": ("Define optional parameters and defaults. Asset-specific values can override these defaults.", "Optionale Parameter und Standardwerte definieren. Asset-spezifische Werte können diese Standards überschreiben."),
    "jobparams.add": ("Add parameter", "Parameter hinzufügen"),
    "jobparams.key": ("Technical key", "Technischer Schlüssel"),
    "jobparams.label": ("Label", "Bezeichnung"),
    "jobparams.type": ("Type", "Typ"),
    "jobparams.default": ("Default value", "Standardwert"),
    "jobparams.required": ("Required", "Erforderlich"),
    "jobparams.asset_title": ("Job parameters", "Jobparameter"),
    "jobparams.asset_help": ("Optional asset-specific values override the current defaults of the job definition.", "Optionale Assetwerte überschreiben die aktuellen Standardwerte der Jobdefinition."),
    "jobparams.override": ("Use asset-specific value", "Asset-spezifischen Wert verwenden"),
    "jobparams.default_value": ("Default", "Standard"),
    "jobparams.source": ("Source", "Herkunft"),
    "jobparams.source.asset": ("Asset", "Asset"),
    "jobparams.source.default": ("Default", "Standard"),
    "jobparams.saved": ("Asset-specific job parameters were saved.", "Asset-spezifische Jobparameter wurden gespeichert."),
    "jobparams.none_defined": ("This job definition has no configurable parameters.", "Diese Jobdefinition besitzt keine konfigurierbaren Parameter."),
    "jobparams.parameter_count": ("{count} parameter(s)", "{count} Parameter"),
    "jobs.run_definition": ("Run job", "Job ausführen"),
    "jobs.run_definition_help": ("Only active job definitions matching the detected platform of this asset are shown.", "Es werden nur aktive Jobdefinitionen angezeigt, die zur erkannten Plattform dieses Assets passen."),
    "jobs.select_definition": ("Select job definition", "Jobdefinition auswählen"),
    "jobs.no_compatible_definitions": ("No active job definition is available for this platform.", "Für diese Plattform ist keine aktive Jobdefinition verfügbar."),
    "jobs.single.confirm": ("Start the selected job on this asset now?", "Den ausgewählten Job jetzt auf diesem Asset starten?"),
    "jobs.started": ("Job started.", "Job wurde gestartet."),
    "jobs.event.created_named": ('Job "{name}" created', 'Job „{name}“ wurde erstellt'),
    "jobs.definition_unavailable": ("The selected job definition is not available.", "Die ausgewählte Jobdefinition ist nicht verfügbar."),
    "jobs.asset_missing_node": ("The asset has no MeshCentral node ID.", "Das Asset besitzt keine MeshCentral Node-ID."),
    "jobs.asset_unknown_platform": ("The operating system platform could not be detected.", "Die Betriebssystemplattform konnte nicht erkannt werden."),
    "jobs.platform_mismatch": ("The job definition does not match the asset platform.", "Die Jobdefinition passt nicht zur Plattform des Assets."),
    "jobdefs.kind": ("Job type", "Jobart"),
    "jobdefs.kind.help": ("Script jobs contain your own code. Registry jobs are configured declaratively and generate PowerShell at runtime.", "Skriptjobs enthalten eigenen Code. Registry-Jobs werden deklarativ konfiguriert und erzeugen PowerShell erst zur Laufzeit."),
    "jobdefs.kind.script": ("Script", "Skript"),
    "jobdefs.kind.registry": ("Windows Registry", "Windows-Registry"),
    "jobdefs.validation.registry_empty": ("At least one registry entry is required.", "Mindestens ein Registry-Eintrag ist erforderlich."),
    "registry.title": ("Registry entries", "Registry-Einträge"),
    "registry.help": ("Set or remove multiple Windows Registry values and keys. Missing keys are created automatically when values are set.", "Mehrere Windows-Registry-Werte und Schlüssel setzen oder entfernen. Fehlende Schlüssel werden beim Setzen automatisch angelegt."),
    "registry.add": ("Add registry entry", "Registry-Eintrag hinzufügen"),
    "registry.backup": ("Export affected keys locally before changes", "Betroffene Schlüssel vor Änderungen lokal exportieren"),
    "registry.action": ("Action", "Aktion"),
    "registry.action.set": ("Set value", "Wert setzen"),
    "registry.action.delete_value": ("Delete value", "Wert löschen"),
    "registry.action.delete_key": ("Delete key recursively", "Schlüssel rekursiv löschen"),
    "registry.action.set_permissions": ("Set key permissions", "Schlüsselberechtigungen setzen"),
    "registry.operation": ("What should this entry do?", "Was soll dieser Eintrag tun?"),
    "registry.action.set.help": ("Create or update one registry value.", "Einen Registry-Wert anlegen oder aktualisieren."),
    "registry.action.set_permissions.help": ("Set permissions on the selected registry key.", "Berechtigungen für den ausgewählten Registry-Schlüssel setzen."),
    "registry.action.delete_value.help": ("Remove one value while keeping the key.", "Einen Wert entfernen, den Schlüssel aber behalten."),
    "registry.action.delete_key.help": ("Remove the key including all subkeys.", "Den Schlüssel einschließlich aller Unterschlüssel entfernen."),
    "registry.principal": ("User, group or SID", "Benutzer, Gruppe oder SID"),
    "registry.principal_help": ("Use a SID for language-independent assignments. Built-in Users: S-1-5-32-545.", "Für sprachunabhängige Zuweisungen eine SID verwenden. Integrierte Benutzer: S-1-5-32-545."),
    "registry.principal.users": ("Users (built-in)", "Benutzer (integriert)"),
    "registry.principal.admins": ("Administrators (built-in)", "Administratoren (integriert)"),
    "registry.principal.authenticated": ("Authenticated users", "Authentifizierte Benutzer"),
    "registry.principal.everyone": ("Everyone", "Jeder"),
    "registry.principal_custom": ("SID, user or group", "SID, Benutzer oder Gruppe"),
    "registry.custom": ("Custom...", "Benutzerdefiniert …"),
    "registry.rights": ("Registry rights", "Registry-Berechtigung"),
    "registry.rights.read": ("Read", "Lesen"),
    "registry.rights.change": ("Change", "Ändern"),
    "registry.rights.full": ("Full control", "Vollzugriff"),
    "registry.rights_custom": ("Custom .NET RegistryRights", "Benutzerdefinierte .NET-RegistryRights"),
    "registry.access_type": ("Access type", "Zugriffstyp"),
    "registry.inherit_subkeys": ("Apply to subkeys", "Auf Unterschlüssel vererben"),
    "registry.replace_existing": ("Replace matching explicit rule", "Passende explizite Regel ersetzen"),
    "registry.hive": ("Hive", "Hive"),
    "registry.path": ("Registry path", "Registry-Pfad"),
    "registry.path.help": ("Enter the path below the selected hive, for example SOFTWARE\\Vendor\\Product.", "Pfad unterhalb des ausgewählten Hives eingeben, zum Beispiel SOFTWARE\\Hersteller\\Produkt."),
    "registry.name": ("Value name", "Wertname"),
    "registry.type": ("Value type", "Werttyp"),
    "registry.value": ("Value or parameter", "Wert oder Parameter"),
    "registry.only_if_missing": ("Only set when the value is missing", "Nur setzen, wenn der Wert fehlt"),
    "registry.set_value_enabled": ("Set registry value", "Registry-Wert setzen"),
    "registry.permissions_enabled": ("Change permissions for this key", "Berechtigungen für diesen Schlüssel ändern"),
    "registry.parameters.title": ("Parameters in Registry entries", "Parameter in Registry-Einträgen"),
    "registry.parameters.body": ("Define technical parameter keys in the Parameter section below. They apply to the complete job definition and can be reused in any Registry path, value, custom identity or custom rights field. Separate per-entry parameter definitions are not required.", "Technische Parameterschlüssel werden unten im Bereich Parameter definiert. Sie gelten für die gesamte Jobdefinition und können in jedem Registry-Pfad, Wert sowie in benutzerdefinierten Identitäts- und Berechtigungsfeldern wiederverwendet werden. Eigene Parameterdefinitionen pro Registry-Eintrag sind nicht nötig."),
    "registry.parameters.available": ("Available keys:", "Verfügbare Schlüssel:"),
    "registry.parameters.none": ("No parameters defined yet.", "Noch keine Parameter definiert."),
    "registry.notes.title": ("Variables and behavior", "Variablen und Verhalten"),
    "registry.notes.body": ("Paths, values, identities and rights can contain job parameters such as {{Param.RegistryPath}} or {{Param.RegistryRights}}. Setting values and changing permissions create missing keys. Use SIDs for language-independent identities. Registry jobs run in the MeshCentral agent context; HKCU therefore refers to that execution account.", "Pfade, Werte, Identitäten und Berechtigungen können Jobparameter wie {{Param.RegistryPath}} oder {{Param.RegistryRights}} enthalten. Beim Setzen von Werten und Ändern von Berechtigungen werden fehlende Schlüssel angelegt. Für sprachunabhängige Identitäten sollten SIDs verwendet werden. Registry-Jobs laufen im Kontext des MeshCentral-Agenten; HKCU bezieht sich daher auf dieses Ausführungskonto."),
    "common.disabled": ("disabled", "deaktiviert"),
    "common.days": ("days", "Tage"),
    "assets.job_filter.button": ("Filter by job status", "Jobstatus filtern"),
    "assets.job_filter.title": ("Filter assets by job status", "Assets nach Jobstatus filtern"),
    "assets.job_filter.help": ("Job information is loaded only while this filter is active. The normal asset list remains unchanged and fast.", "Die Jobinformationen werden nur bei aktivem Filter geladen. Die normale Assetliste bleibt unverändert schnell."),
    "assets.job_filter.job": ("Job", "Job"),
    "assets.job_filter.jobs": ("Jobs", "Jobs"),
    "assets.job_filter.jobs_help": ("Multiple jobs are treated as alternative executions. The most recently executed selected job determines the displayed status.", "Mehrere Jobs werden als alternative Ausführungen zusammengefasst. Maßgeblich ist der zuletzt ausgeführte ausgewählte Job."),
    "assets.job_filter.select_at_least_one": ("Select at least one job.", "Bitte mindestens einen Job auswählen."),
    "assets.job_filter.start_job": ("Job to run", "Auszuführender Job"),
    "assets.job_filter.start_invalid": ("Select one of the filtered jobs to run.", "Wähle einen der gefilterten Jobs zur Ausführung aus."),
    "assets.job_filter.condition": ("Condition", "Bedingung"),
    "assets.job_filter.condition.successful": ("Successfully completed at least once", "Mindestens einmal erfolgreich durchgeführt"),
    "assets.job_filter.condition.not_successful": ("Not yet completed successfully", "Noch nicht erfolgreich durchgeführt"),
    "assets.job_filter.condition.never_run": ("Never run", "Noch nie ausgeführt"),
    "assets.job_filter.condition.last_failed": ("Last status: failed", "Letzter Status: fehlgeschlagen"),
    "assets.job_filter.condition.last_partial": ("Last status: partially successful", "Letzter Status: teilweise erfolgreich"),
    "assets.job_filter.condition.timeout": ("Last status: timed out", "Letzter Status: Zeitüberschreitung"),
    "assets.job_filter.condition.callback_pending": ("Callback pending", "Callback ausstehend"),
    "assets.job_filter.condition.active": ("Currently active or queued", "Aktuell aktiv oder eingereiht"),
    "assets.job_filter.condition.success_older": ("Last success older than", "Letzter Erfolg älter als"),
    "assets.job_filter.days": ("Success older than days", "Erfolg älter als Tage"),
    "assets.job_filter.current_revision": ("current revision only", "nur aktuelle Revision"),
    "assets.job_filter.apply": ("Apply filter", "Filter anwenden"),
    "assets.job_filter.clear": ("Remove job filter", "Jobfilter entfernen"),
    "assets.job_filter.active": ("Active job filter", "Aktiver Jobfilter"),
    "assets.job_filter.status": ("Job status", "Jobstatus"),
    "assets.job_filter.last_execution": ("Last execution", "Letzte Ausführung"),
    "assets.job_filter.last_success": ("Last success", "Letzter Erfolg"),
    "assets.job_filter.revision": ("Revision", "Revision"),
    "assets.job_filter.never_run": ("Never run", "Noch nie ausgeführt"),
    "software.status.never_run": ("Never run", "Noch nie ausgeführt"),
    "assets.job_filter.start": ("Run job for filtered assets", "Job für gefilterte Assets starten"),
    "assets.job_filter.start_confirm": ("Create and start the selected job for all {count} filtered assets?", "Den ausgewählten Job für alle {count} gefilterten Assets erstellen und starten?"),
    "assets.job_filter.invalid": ("The job filter is invalid.", "Der Jobfilter ist ungültig."),
    "assets.job_filter.no_assets": ("No assets match this job filter.", "Für diesen Jobfilter wurden keine Assets gefunden."),
    "assets.job_filter.started": ("Created {created} jobs for the filtered assets; skipped {skipped} assets.", "Für die gefilterten Assets wurden {created} Jobs erstellt; {skipped} Assets wurden übersprungen."),
    "assets.job_filter.excluded_statuses": ("Excluded statuses", "Auszuschließende Status"),
    "assets.job_filter.excluded_help": ("Checked statuses are excluded from the asset list. With no selection, all assets are shown.", "Markierte Status werden nicht in der Assetliste angezeigt. Ohne Markierung werden alle Assets angezeigt."),
    "assets.job_filter.excluded_summary": ("Excluded", "Ausgeschlossen"),
    "assets.job_filter.excluded_none": ("no statuses", "keine Status"),
    "assets.job_filter.exclude_all": ("Select all", "Alle markieren"),
    "assets.job_filter.exclude_none": ("Clear selection", "Auswahl aufheben"),
    "job_states.open": ("Job status by asset", "Jobstatus je Asset"),
    "job_states.title": ("Job status by asset", "Jobstatus je Asset"),
    "job_states.description": ("Current compact job status per asset and job. The complete history remains available in the individual jobs.", "Aktueller kompakter Jobstatus pro Asset und Job. Die vollständige Historie bleibt in den einzelnen Jobs erhalten."),
    "job_states.back": ("Back to job overview", "Zur Jobübersicht"),
    "job_states.job": ("Job", "Job"),
    "job_states.revision": ("Latest revision", "Letzte Revision"),
    "job_states.success_revision": ("Successful revision", "Erfolgsrevision"),
    "job_states.execution_count": ("Executions", "Ausführungen"),
    "job_states.last_job": ("Latest job", "Letzter Job"),
    "job_states.updated_at": ("Updated", "Aktualisiert"),
    "job_states.empty": ("No job status data is available yet.", "Noch keine Jobstatusdaten vorhanden."),
})

BASE_TRANSLATIONS.update({
    "mesh.link.section_title": ("MeshCentral link", "MeshCentral-Verknüpfung"),
    "mesh.link.section_help": ("The node ID can be changed manually without deleting the asset. Other asset fields are updated only by the next MeshCentral synchronization.", "Die Node-ID kann manuell geändert werden, ohne das Asset zu löschen. Andere Assetfelder werden erst bei der nächsten MeshCentral-Synchronisierung aktualisiert."),
    "mesh.link.title": ("Change MeshCentral link", "MeshCentral-Verknüpfung ändern"),
    "mesh.link.page_help": ("Select an unassigned MeshCentral device. The node ID is changed immediately; other asset fields are updated only during the next synchronization.", "Wähle ein noch nicht zugeordnetes MeshCentral-Gerät aus. Die Node-ID wird sofort geändert; andere Assetfelder werden erst bei der nächsten Synchronisierung aktualisiert."),
    "mesh.link.change": ("Change link", "Verknüpfung ändern"),
    "mesh.link.initialize": ("Initialize link", "Verknüpfung initialisieren"),
    "mesh.link.unlink": ("Remove link", "Verknüpfung lösen"),
    "mesh.link.select_existing": ("Select MeshCentral device", "MeshCentral-Gerät auswählen"),
    "mesh.link.clear_field": ("Clear node ID", "Node-ID leeren"),
    "mesh.link.edit_help": ("The node ID can be edited directly. An empty field removes the link when saved. Other asset data is updated only during the next MeshCentral synchronization.", "Die Node-ID kann direkt bearbeitet werden. Ein leeres Feld löst die Verknüpfung beim Speichern. Andere Assetdaten werden erst beim nächsten MeshCentral-Sync aktualisiert."),
    "mesh.link.unlink_confirm": ("Remove the MeshCentral link from this asset? The asset itself and its stored data remain unchanged.", "Die MeshCentral-Verknüpfung dieses Assets wirklich lösen? Das Asset selbst und seine gespeicherten Daten bleiben erhalten."),
    "mesh.link.asset_comparison": ("Asset and current link", "Asset und aktuelle Verknüpfung"),
    "mesh.link.current_device": ("Current MeshCentral device", "Aktuelles MeshCentral-Gerät"),
    "mesh.link.current_missing": ("The currently stored node ID was not found in MeshCentral. You can replace it with one of the available devices below.", "Die aktuell gespeicherte Node-ID wurde in MeshCentral nicht gefunden. Du kannst sie durch eines der unten verfügbaren Geräte ersetzen."),
    "mesh.link.available_devices": ("Available MeshCentral devices", "Verfügbare MeshCentral-Geräte"),
    "mesh.link.available_help": ("Only node IDs that are not assigned to another asset are shown.", "Es werden nur Node-IDs angezeigt, die keinem anderen Asset zugeordnet sind."),
    "mesh.link.select_device": ("Select device", "Gerät auswählen"),
    "mesh.link.select_required": ("Select a MeshCentral device.", "Bitte ein MeshCentral-Gerät auswählen."),
    "mesh.link.change_confirm": ("Assign the selected MeshCentral device to this asset?", "Das ausgewählte MeshCentral-Gerät diesem Asset zuordnen?"),
    "mesh.link.save": ("Save link", "Verknüpfung speichern"),
    "mesh.link.no_available_devices": ("No unassigned MeshCentral devices are currently available.", "Aktuell sind keine noch nicht zugeordneten MeshCentral-Geräte verfügbar."),
    "mesh.link.fetch_failed": ("The MeshCentral device list could not be loaded: {error}", "Die MeshCentral-Geräteliste konnte nicht geladen werden: {error}"),
    "mesh.link.not_found": ("The selected MeshCentral device no longer exists.", "Das ausgewählte MeshCentral-Gerät existiert nicht mehr."),
    "mesh.link.already_assigned": ("This MeshCentral device is already linked to asset {asset}.", "Dieses MeshCentral-Gerät ist bereits dem Asset {asset} zugeordnet."),
    "mesh.link.unchanged": ("The MeshCentral link is already set to this device.", "Die MeshCentral-Verknüpfung ist bereits auf dieses Gerät gesetzt."),
    "mesh.link.pending_sync": ("MeshCentral link changed manually; synchronization is pending.", "MeshCentral-Verknüpfung manuell geändert; Synchronisierung ausstehend."),
    "mesh.link.conflict": ("The MeshCentral node ID was assigned by another operation.", "Die MeshCentral Node-ID wurde zwischenzeitlich durch einen anderen Vorgang zugeordnet."),
    "mesh.link.changed": ("MeshCentral link changed.", "MeshCentral-Verknüpfung geändert."),
    "mesh.link.already_unlinked": ("This asset is not linked to MeshCentral.", "Dieses Asset ist nicht mit MeshCentral verknüpft."),
    "mesh.link.unlinked_message": ("MeshCentral link removed manually.", "MeshCentral-Verknüpfung manuell gelöst."),
    "mesh.link.unlinked": ("MeshCentral link removed.", "MeshCentral-Verknüpfung gelöst."),
})

BASE_TRANSLATIONS.update({
    "mesh.sync_status.pending": ("Synchronization pending", "Synchronisierung ausstehend"),
    "mesh.sync_status.unlinked": ("Not linked", "Nicht verknüpft"),
    "mesh.sync_status.synced": ("Synchronized", "Synchronisiert"),
    "mesh.sync_status.updated": ("Updated", "Aktualisiert"),
    "mesh.sync_status.created": ("Created", "Neu angelegt"),
    "mesh.sync_status.conflict": ("Conflict", "Konflikt"),
    "mesh.sync_status.missing": ("Missing in MeshCentral", "In MeshCentral nicht gefunden"),
    "settings.ldap_debug_logging": ("Enable detailed LDAP debug logging", "Ausführliches LDAP-Debug-Protokoll aktivieren"),
    "settings.ldap_debug_logging_help": ("When disabled, the LDAP log records only the searched username, whether it was found, and whether authentication succeeded. Enable this only temporarily for troubleshooting.", "Wenn deaktiviert, protokolliert LDAP nur den gesuchten Benutzernamen, ob er gefunden wurde und ob die Anmeldung erfolgreich war. Nur vorübergehend zur Fehlersuche aktivieren."),

})

BASE_TRANSLATIONS.update({
    "software.compare.button": ("Compare software", "Software vergleichen"),
    "software.compare.title": ("Compare software inventories", "Softwareinventuren vergleichen"),
    "software.compare.subtitle": ("Comparison of {asset_a} and {asset_b}", "Vergleich von {asset_a} und {asset_b}"),
    "software.compare.export": ("Download as Excel", "Als Excel herunterladen"),
    "software.compare.mode": ("Comparison mode", "Vergleichsmodus"),
    "software.compare.mode.exact": ("Exact software name", "Exakter Softwarename"),
    "software.compare.mode.ignore_version": ("Ignore versions", "Versionen ignorieren"),
    "software.compare.mode.ignore_version_year": ("Ignore versions and years", "Versionen und Jahreszahlen ignorieren"),
    "software.compare.mode_help": ("Original names and versions remain visible. Normalization is used only for matching.", "Originalnamen und Versionen bleiben sichtbar. Die Normalisierung wird nur für die Zuordnung verwendet."),
    "software.compare.only_a": ("Only on {asset}", "Nur auf {asset}"),
    "software.compare.only_b": ("Only on {asset}", "Nur auf {asset}"),
    "software.compare.both": ("Present on both", "Auf beiden vorhanden"),
    "software.compare.comparison_name": ("Comparison name", "Vergleichsname"),
    "software.compare.status": ("Comparison status", "Vergleichsstatus"),
    "software.compare.empty": ("No software inventory is available for these assets.", "Für diese Assets sind keine Softwareinventuren vorhanden."),
})

BASE_TRANSLATIONS.update({
    "common.add": ("Add", "Hinzufügen"),
    "privacy.menu": ("Security & privacy", "Sicherheit & Datenschutz"),
    "privacy.title": ("Security & privacy", "Sicherheit & Datenschutz"),
    "privacy.intro": ("Document processing activities, personal-data categories, retention criteria, manual deletion procedures, recipients, transfers and technical safeguards.", "Dokumentiere Verarbeitungstätigkeiten, Kategorien personenbezogener Daten, Löschkriterien, manuelle Löschverfahren, Empfänger, Übermittlungen und technische Schutzmaßnahmen."),
    "privacy.export_text": ("Export text report", "Textbericht exportieren"),
    "privacy.export_excel": ("Export Excel report", "Excel-Bericht exportieren"),
    "privacy.export_html": ("Export HTML report", "HTML-Bericht exportieren"),
    "privacy.phase1_title": ("Phase 1: documentation only.", "Stufe 1: nur Dokumentation."),
    "privacy.phase1_help": ("Automatic deletion is not executed in this version. Settings describe desired retention and prepare later dry-run and deletion functions.", "In dieser Version werden keine automatischen Löschungen ausgeführt. Die Einstellungen dokumentieren die gewünschten Fristen und bereiten spätere Prüfläufe und Löschfunktionen vor."),
    "privacy.processing_activity": ("Processing activity", "Verarbeitungstätigkeit"),
    "privacy.activity_title": ("Title of processing activity", "Bezeichnung der Verarbeitungstätigkeit"),
    "privacy.document_version": ("Document version", "Dokumentversion"),
    "privacy.controller": ("Controller / organisation", "Verantwortlicher / Organisation"),
    "privacy.responsible_department": ("Responsible department", "Verantwortlicher Fachbereich"),
    "privacy.responsible_people": ("Responsible people", "Verantwortliche Personen"),
    "privacy.data_protection_contact": ("Data protection contact", "Datenschutzkontakt"),
    "privacy.legal_basis": ("Legal basis", "Rechtsgrundlage"),
    "privacy.data_subject_groups": ("Data-subject groups", "Betroffene Personengruppen"),
    "privacy.purposes": ("Purposes", "Zwecke"),
    "privacy.purposes_help": ("List the purposes of processing separately.", "Führe die Verarbeitungszwecke einzeln auf."),
    "privacy.recipients_transfers": ("Recipients and transfers", "Empfänger und Übermittlungen"),
    "privacy.internal_recipients": ("Internal recipients", "Interne Empfänger"),
    "privacy.external_recipients": ("External recipients", "Externe Empfänger"),
    "privacy.third_country_transfer": ("Transfer to a third country or international organisation", "Übermittlung in ein Drittland oder an eine internationale Organisation"),
    "privacy.third_country_details": ("Transfer details and safeguards", "Angaben zur Übermittlung und zu Garantien"),
    "privacy.systems_backups": ("Systems and backups", "Systeme und Sicherungen"),
    "privacy.systems": ("Systems and data locations", "Systeme und Speicherorte"),
    "privacy.backup_system": ("Backup system", "Backup-System"),
    "privacy.backup_retention_days": ("Backup retention in days", "Backup-Aufbewahrung in Tagen"),
    "privacy.restore_deletion_procedure": ("Deletion procedure after restore", "Löschverfahren nach einer Wiederherstellung"),
    "privacy.data_categories": ("Data categories and retention", "Datenkategorien und Löschfristen"),
    "privacy.data_categories_help": ("Define storage, overwrite and deletion criteria per category. Automatic deletion remains disabled in phase 1.", "Definiere Speicherung, Überschreibung und Löschkriterien je Kategorie. Die automatische Löschung bleibt in Stufe 1 deaktiviert."),
    "privacy.data_categories_tabs_help": ("Each data category is displayed as a separate worksheet-style tab. Tabs can be added, edited and removed.", "Jede Datenkategorie wird als eigener Karteireiter dargestellt. Karteireiter können hinzugefügt, bearbeitet und gelöscht werden."),
    "privacy.add_category": ("Add data category", "Datenkategorie hinzufügen"),
    "privacy.remove_category": ("Remove category", "Kategorie entfernen"),
    "privacy.new_category": ("New data category", "Neue Datenkategorie"),
    "privacy.category_name": ("Category name", "Bezeichnung der Kategorie"),
    "privacy.system": ("System", "System"),
    "privacy.data_subjects": ("Data subjects", "Betroffene Personen"),
    "privacy.personal_data": ("Personal data", "Personenbezogene Daten"),
    "privacy.purpose": ("Purpose", "Zweck"),
    "privacy.retention_type": ("Retention/deletion type", "Aufbewahrungs-/Löschart"),
    "privacy.retention_days": ("Retention in days", "Aufbewahrung in Tagen"),
    "privacy.retention_trigger": ("Start/trigger of the period", "Beginn/Auslöser der Frist"),
    "privacy.automatic_possible": ("Automatic deletion technically possible", "Automatische Löschung technisch möglich"),
    "privacy.manual_procedure": ("Manual deletion procedure", "Manuelles Löschverfahren"),
    "privacy.backup_handling": ("Backup handling", "Behandlung in Sicherungen"),
    "privacy.responsible": ("Responsible role", "Verantwortliche Rolle"),
    "privacy.notes": ("Notes", "Hinweise"),
    "privacy.retention_days_type": ("Delete/anonymise after a number of days", "Nach einer Anzahl von Tagen löschen/anonymisieren"),
    "privacy.retention_manual_parent": ("Manual deletion with parent record", "Manuelle Löschung mit übergeordnetem Datensatz"),
    "privacy.retention_overwrite_parent": ("Overwrite current value; delete with parent record", "Aktuellen Wert überschreiben; mit übergeordnetem Datensatz löschen"),
    "privacy.retention_external_days": ("External system: retention in days", "Externes System: Aufbewahrung in Tagen"),
    "privacy.retention_external_manual": ("External system: manual deletion", "Externes System: manuelle Löschung"),
    "privacy.retention_external_overwrite": ("External system: overwrite and parent deletion", "Externes System: Überschreiben und Löschung mit Datensatz"),
    "privacy.retention_not_stored": ("Not stored permanently", "Nicht dauerhaft gespeichert"),
    "privacy.retention_manual": ("Manual procedure", "Manuelles Verfahren"),
    "privacy.toms": ("Technical and organisational measures", "Technische und organisatorische Maßnahmen"),
    "privacy.toms_help": ("Document access control, encryption, backups, logging, patching and operational safeguards.", "Dokumentiere Zugriffskontrolle, Verschlüsselung, Sicherungen, Protokollierung, Patchmanagement und organisatorische Maßnahmen."),
    "privacy.review": ("Review and approval", "Prüfung und Freigabe"),
    "privacy.review_interval_months": ("Review interval in months", "Prüfintervall in Monaten"),
    "privacy.last_reviewed_at": ("Last reviewed", "Zuletzt geprüft"),
    "privacy.approved_by": ("Approved by", "Freigegeben durch"),
    "privacy.policy_audit": ("Policy audit log", "Änderungsprotokoll des Datenschutzkonzepts"),
    "privacy.policy_audit_help": ("Records policy changes and exports without storing the deleted personal data itself.", "Protokolliert Änderungen und Exporte, ohne die gelöschten personenbezogenen Inhalte selbst zu speichern."),
    "privacy.action": ("Action", "Aktion"),
    "privacy.changed_by": ("Changed by", "Bearbeiter"),
    "privacy.details": ("Details", "Details"),
    "privacy.no_audit_entries": ("No policy changes have been recorded yet.", "Noch keine Änderungen am Datenschutzkonzept protokolliert."),
    "privacy.saved": ("Privacy settings saved.", "Datenschutz-Einstellungen gespeichert."),
    "privacy.phase2_title": ("Phase 2: checks and manual deletion.", "Stufe 2: Prüfläufe und manuelle Löschung."),
    "privacy.phase2_help": ("Automatic deletion remains disabled. Administrators can run checks and manually confirmed deletion runs for selected protected system categories.", "Die automatische Löschung bleibt deaktiviert. Administratoren können für ausgewählte geschützte Systemkategorien Prüfläufe und manuell bestätigte Löschläufe ausführen."),
    "privacy.read_only_title": ("Read-only access.", "Nur-Lese-Zugriff."),
    "privacy.read_only_help": ("Only administrators may change privacy settings or execute retention actions.", "Nur Administratoren dürfen Datenschutz-Einstellungen ändern oder Löschaktionen ausführen."),
    "privacy.system_category_help": ("Protected system category. It cannot be removed because application retention functions depend on its stable identifier.", "Geschützte Systemkategorie. Sie kann nicht entfernt werden, da Löschfunktionen der Anwendung von ihrer festen Kennung abhängen."),
    "privacy.run_check": ("Run check", "Prüflauf starten"),
    "privacy.run_manual_deletion": ("Run manual deletion", "Manuelle Löschung ausführen"),
    "privacy.handler_not_implemented": ("No executable retention handler is implemented yet.", "Für diese Kategorie ist noch keine ausführbare Löschfunktion implementiert."),
    "privacy.check_result": ("Last check result", "Letztes Prüfergebnis"),
    "privacy.matched_records": ("Matched records/files", "Betroffene Datensätze/Dateien"),
    "privacy.approx_size": ("Approximate size", "Ungefähre Größe"),
    "privacy.cutoff": ("Cutoff UTC", "Stichtag UTC"),
    "privacy.check_completed": ("Retention check completed.", "Prüflauf abgeschlossen."),
    "privacy.check_not_supported": ("No executable retention handler is available for this category.", "Für diese Kategorie ist keine ausführbare Löschfunktion verfügbar."),
    "privacy.delete_confirmation_prompt": ("Enter DELETE to permanently execute the retention action for {category}.", "Gib DELETE ein, um die Löschaktion für {category} dauerhaft auszuführen."),
    "privacy.delete_confirmation_invalid": ("Enter DELETE to confirm the deletion run.", "Gib DELETE ein, um den Löschlauf zu bestätigen."),
    "privacy.delete_completed": ("Manual deletion run completed.", "Manueller Löschlauf abgeschlossen."),
    "privacy.deletion_audit": ("Deletion run log", "Löschprotokoll"),
    "privacy.deletion_audit_help": ("Records checks and deletion runs without storing the deleted personal content itself.", "Protokolliert Prüfläufe und Löschläufe, ohne die gelöschten personenbezogenen Inhalte selbst zu speichern."),
    "privacy.run_id": ("Run ID", "Lauf-ID"),
    "privacy.no_deletion_entries": ("No checks or deletion runs have been recorded yet.", "Noch keine Prüfläufe oder Löschläufe protokolliert."),
})
