2025-12-06 13:58:46 +00:00
from typing import Dict
AVAILABLE_LANGUAGES : Dict [ str , str ] = {
" de " : " Deutsch " ,
" en " : " English " ,
}
translations : Dict [ str , Dict [ str , str ] ] = {
" de " : {
" brand.tagline " : " Deine gemieteten Server im Blick " ,
" nav.admin " : " Admin-Dashboard " ,
" nav.users " : " User " ,
" nav.overview " : " Übersicht " ,
" nav.map " : " Karte " ,
2025-12-06 14:32:23 +00:00
" nav.archive " : " Archiv " ,
2025-12-06 13:58:46 +00:00
" nav.new_server " : " Server anlegen " ,
" nav.logout " : " Logout " ,
" nav.login " : " Login " ,
" nav.register " : " Registrieren " ,
" nav.logged_in_as " : " Eingeloggt als " ,
" nav.console " : " Konsole öffnen " ,
" footer.left " : " Selfhosted VPS overview " ,
" footer.right " : " PWA ready · Dark Mode " ,
" warning.no_encryption " : " Hinweis: ENCRYPTION_KEY nicht gesetzt – Passwörter werden nicht gespeichert. " ,
" warning.no_encryption_short " : " ENCRYPTION_KEY ist nicht gesetzt – eingegebene Management-Passwörter werden nicht gespeichert. " ,
" server_list.title " : " Deine Server " ,
" server_list.subtitle " : " Übersicht aller gemieteten VPS, dedizierten Server und Storage-Systeme. " ,
" stats.total " : " Gesamt " ,
" stats.active " : " aktive Server " ,
" stats.costs " : " Laufende Kosten " ,
" stats.per_month " : " pro Monat " ,
" stats.mixed_currencies " : " (gemischte Währungen) " ,
" stats.expiring_soon " : " Laufen bald aus " ,
" stats.expired " : " Abgelaufen " ,
" stats.expiring_soon_hint " : " ≤ 30 Tage " ,
" stats.expired_hint " : " Vertrag beendet " ,
" table.name " : " Name " ,
" table.provider " : " Provider " ,
" table.type " : " Typ " ,
" table.location " : " Location " ,
" table.ipv4 " : " IPv4 " ,
" table.costs " : " Kosten " ,
" table.action " : " Aktion " ,
" status.expired " : " abgelaufen " ,
" status.expiring " : " läuft bald aus " ,
" server_list.empty " : " Noch keine Server erfasst. Leg den ersten mit „Server anlegen“ oben rechts an. " ,
" price.month " : " Monat " ,
" price.year " : " Jahr " ,
2025-12-06 14:32:23 +00:00
" archive.title " : " Archivierte Server " ,
" archive.subtitle " : " Ausgeblendete (archivierte) Server. Hier kannst du sie wiederherstellen. " ,
" archive.restore " : " Wiederherstellen " ,
" archive.empty " : " Keine archivierten Server vorhanden. " ,
2025-12-06 13:58:46 +00:00
" server_detail.server " : " Server " ,
" server_detail.contract_end " : " Vertragsende " ,
" server_detail.days_ago " : " vor {days} Tagen " ,
" server_detail.today " : " heute " ,
" server_detail.in_days " : " in {days} Tagen " ,
" server_detail.edit " : " Bearbeiten " ,
" server_detail.archive " : " Archivieren " ,
" server_detail.archive_confirm " : " Diesen Server archivieren? " ,
" section.net_costs " : " Netz & Kosten " ,
" section.hardware " : " Hardware " ,
" section.access " : " Zugänge " ,
" section.notes " : " Notizen " ,
" field.ipv4 " : " IPv4 " ,
" field.ipv6 " : " IPv6 " ,
" field.costs " : " Kosten " ,
" field.contract " : " Vertrag " ,
" field.cpu " : " CPU " ,
" field.ram " : " RAM " ,
" field.storage " : " Storage " ,
" field.management " : " Management " ,
" field.mgmt_user " : " Mgmt-User " ,
" field.mgmt_password " : " Mgmt-Passwort " ,
" field.ssh_user " : " SSH User " ,
" field.ssh_key_hint " : " SSH Key Hint " ,
" note.ssh_keys " : " Hinweis: Es werden nur Key-Namen gespeichert, keine privaten SSH-Schlüssel. " ,
" notes.empty " : " Keine Notizen. " ,
" mgmt.password_encrypted_missing " : " verschlüsselt gespeichert (Key fehlt?) " ,
" back.overview " : " Zurück zur Übersicht " ,
" updated_at " : " Zuletzt aktualisiert: " ,
" form.title.new " : " Neuen Server anlegen " ,
" form.title.edit " : " Server bearbeiten " ,
" form.subtitle " : " Trage alle relevanten Infos zu deinem VPS / Server ein. " ,
" section.general " : " Allgemein " ,
" label.name " : " Name " ,
" label.provider " : " Provider " ,
" label.hostname " : " Hostname " ,
" label.type " : " Typ " ,
" label.location " : " Location " ,
" label.tags " : " Tags (kommagetrennt) " ,
" label.ipv4 " : " IPv4 " ,
" label.ipv6 " : " IPv6 " ,
" section.network " : " Netzwerk " ,
" section.costs " : " Kosten " ,
" label.amount " : " Betrag " ,
" label.currency " : " Währung " ,
" label.billing " : " Abrechnung " ,
" label.contract_start " : " Vertragsbeginn " ,
" label.contract_end " : " Vertragsende " ,
" section.hardware.small " : " Hardware " ,
" label.cpu_model " : " CPU-Modell " ,
" label.cpu_cores " : " CPU Cores " ,
" label.ram_mb " : " RAM (MB) " ,
" label.storage_gb " : " Storage (GB) " ,
" label.storage_type " : " Storage-Typ " ,
" section.access.small " : " Zugänge " ,
" hint.ssh " : " SSH: hier nur Key-Namen oder Hints eintragen, keine privaten Keys. " ,
" label.mgmt_url " : " Management URL " ,
" label.mgmt_user " : " Management User " ,
" label.mgmt_password " : " Management Passwort " ,
" label.ssh_user " : " SSH User " ,
" label.ssh_key_hint " : " SSH Key Hint " ,
" section.notes.small " : " Notizen " ,
" btn.cancel " : " Abbrechen " ,
" btn.save " : " Speichern " ,
" btn.save_changes " : " Änderungen speichern " ,
" map.title " : " Server-Karte " ,
" map.subtitle " : " Zeigt alle nicht archivierten Server mit gesetzter Location auf einer Karte. Für grobe Übersicht reicht die Stadt – keine exakten GPS-Daten notwendig. " ,
" map.note " : " Hinweis: Marker werden anhand der Location-Namen grob auf der Weltkarte platziert. Mehrere Server in derselben Stadt werden leicht versetzt dargestellt, damit sie klickbar bleiben. " ,
" admin.title " : " Admin-Dashboard " ,
" admin.subtitle " : " Globale Übersicht über alle nicht archivierten Server und Benutzer. " ,
" admin.users " : " Benutzer " ,
" admin.users_caption " : " Accounts " ,
" admin.servers " : " Server " ,
" admin.servers_caption " : " nicht archiviert " ,
" admin.costs " : " Laufende Kosten " ,
" admin.per_month " : " pro Monat " ,
" admin.contract_status " : " Vertragstatus " ,
" admin.expiring " : " Bald auslaufend " ,
" admin.expired " : " Abgelaufen " ,
" admin.by_provider " : " Nach Provider " ,
" admin.provider " : " Provider " ,
" admin.count " : " Server " ,
" admin.monthly_costs " : " Monatskosten " ,
" admin.expiring_soon " : " Laufen bald aus " ,
" admin.expired_count " : " Abgelaufen " ,
" admin.contracts_expiring " : " Laufen bald aus (≤ 30 Tage) " ,
" admin.contracts_none_soon " : " Keine Verträge laufen in den nächsten 30 Tagen aus. " ,
" admin.contracts_expired " : " Abgelaufene Verträge " ,
" admin.contracts_none_expired " : " Keine abgelaufenen Verträge gefunden. " ,
" users.title " : " Benutzerverwaltung " ,
" users.subtitle " : " Admins können Benutzer aktivieren/deaktivieren. Der eigene Account kann nicht deaktiviert werden. " ,
" users.username " : " Benutzername " ,
" users.email " : " E-Mail " ,
" users.role " : " Rolle " ,
" users.status " : " Status " ,
" users.action " : " Aktion " ,
" role.admin " : " Admin " ,
" role.user " : " User " ,
" status.active " : " aktiv " ,
" status.inactive " : " deaktiviert " ,
" action.deactivate " : " Deaktivieren " ,
" action.activate " : " Aktivieren " ,
" users.own_account " : " Eigener Account " ,
" login.title " : " Login " ,
" login.subtitle " : " Melde dich an, um deine Server zu verwalten. " ,
" login.username " : " Benutzername " ,
" login.password " : " Passwort " ,
" login.no_account " : " Noch kein Account? " ,
" login.register " : " Registrieren " ,
" login.submit " : " Einloggen " ,
" register.title " : " Registrieren " ,
" register.subtitle " : " Erstelle einen neuen Account. Der erste Benutzer wird automatisch Admin. " ,
" register.username " : " Benutzername " ,
" register.email " : " E-Mail (optional) " ,
" register.password " : " Passwort " ,
" register.password_confirm " : " Passwort bestätigen " ,
" register.submit " : " Account anlegen " ,
} ,
" en " : {
" brand.tagline " : " Your rented servers at a glance " ,
" nav.admin " : " Admin Dashboard " ,
" nav.users " : " Users " ,
" nav.overview " : " Overview " ,
" nav.map " : " Map " ,
2025-12-06 14:32:23 +00:00
" nav.archive " : " Archive " ,
2025-12-06 13:58:46 +00:00
" nav.new_server " : " Add server " ,
" nav.logout " : " Logout " ,
" nav.login " : " Login " ,
" nav.register " : " Register " ,
" nav.logged_in_as " : " Logged in as " ,
" nav.console " : " Open console " ,
" footer.left " : " Selfhosted VPS overview " ,
" footer.right " : " PWA ready · Dark Mode " ,
" warning.no_encryption " : " Note: ENCRYPTION_KEY not set – passwords will not be stored. " ,
" warning.no_encryption_short " : " ENCRYPTION_KEY is not set – management passwords will not be stored. " ,
" server_list.title " : " Your servers " ,
" server_list.subtitle " : " Overview of all rented VPS, dedicated servers and storage systems. " ,
" stats.total " : " Total " ,
" stats.active " : " active servers " ,
" stats.costs " : " Recurring costs " ,
" stats.per_month " : " per month " ,
" stats.mixed_currencies " : " (mixed currencies) " ,
" stats.expiring_soon " : " Expiring soon " ,
" stats.expired " : " Expired " ,
" stats.expiring_soon_hint " : " ≤ 30 days " ,
" stats.expired_hint " : " Contract ended " ,
" table.name " : " Name " ,
" table.provider " : " Provider " ,
" table.type " : " Type " ,
" table.location " : " Location " ,
" table.ipv4 " : " IPv4 " ,
" table.costs " : " Costs " ,
" table.action " : " Action " ,
" status.expired " : " expired " ,
" status.expiring " : " expiring soon " ,
" server_list.empty " : " No servers yet. Create the first one via “Add server” in the top right. " ,
" price.month " : " Month " ,
" price.year " : " Year " ,
2025-12-06 14:32:23 +00:00
" archive.title " : " Archived servers " ,
" archive.subtitle " : " Hidden (archived) servers. Restore them here. " ,
" archive.restore " : " Restore " ,
" archive.empty " : " No archived servers found. " ,
2025-12-06 13:58:46 +00:00
" server_detail.server " : " Server " ,
" server_detail.contract_end " : " Contract end " ,
" server_detail.days_ago " : " {days} days ago " ,
" server_detail.today " : " today " ,
" server_detail.in_days " : " in {days} days " ,
" server_detail.edit " : " Edit " ,
" server_detail.archive " : " Archive " ,
" server_detail.archive_confirm " : " Archive this server? " ,
" section.net_costs " : " Network & Costs " ,
" section.hardware " : " Hardware " ,
" section.access " : " Access " ,
" section.notes " : " Notes " ,
" field.ipv4 " : " IPv4 " ,
" field.ipv6 " : " IPv6 " ,
" field.costs " : " Costs " ,
" field.contract " : " Contract " ,
" field.cpu " : " CPU " ,
" field.ram " : " RAM " ,
" field.storage " : " Storage " ,
" field.management " : " Management " ,
" field.mgmt_user " : " Mgmt user " ,
" field.mgmt_password " : " Mgmt password " ,
" field.ssh_user " : " SSH user " ,
" field.ssh_key_hint " : " SSH key hint " ,
" note.ssh_keys " : " Note: Only key names are stored, no private SSH keys. " ,
" notes.empty " : " No notes. " ,
" mgmt.password_encrypted_missing " : " stored encrypted (key missing?) " ,
" back.overview " : " Back to overview " ,
" updated_at " : " Last updated: " ,
" form.title.new " : " Create new server " ,
" form.title.edit " : " Edit server " ,
" form.subtitle " : " Enter all relevant details about your VPS / server. " ,
" section.general " : " General " ,
" label.name " : " Name " ,
" label.provider " : " Provider " ,
" label.hostname " : " Hostname " ,
" label.type " : " Type " ,
" label.location " : " Location " ,
" label.tags " : " Tags (comma-separated) " ,
" label.ipv4 " : " IPv4 " ,
" label.ipv6 " : " IPv6 " ,
" section.network " : " Network " ,
" section.costs " : " Costs " ,
" label.amount " : " Amount " ,
" label.currency " : " Currency " ,
" label.billing " : " Billing " ,
" label.contract_start " : " Contract start " ,
" label.contract_end " : " Contract end " ,
" section.hardware.small " : " Hardware " ,
" label.cpu_model " : " CPU model " ,
" label.cpu_cores " : " CPU cores " ,
" label.ram_mb " : " RAM (MB) " ,
" label.storage_gb " : " Storage (GB) " ,
" label.storage_type " : " Storage type " ,
" section.access.small " : " Access " ,
" hint.ssh " : " SSH: only enter key names or hints, no private keys. " ,
" label.mgmt_url " : " Management URL " ,
" label.mgmt_user " : " Management user " ,
" label.mgmt_password " : " Management password " ,
" label.ssh_user " : " SSH user " ,
" label.ssh_key_hint " : " SSH key hint " ,
" section.notes.small " : " Notes " ,
" btn.cancel " : " Cancel " ,
" btn.save " : " Save " ,
" btn.save_changes " : " Save changes " ,
" map.title " : " Server map " ,
" map.subtitle " : " Shows all non-archived servers with a location on a map. City names are sufficient for rough positioning. " ,
" map.note " : " Note: Markers are placed roughly based on location names. Multiple servers in one city are slightly offset to remain clickable. " ,
" admin.title " : " Admin dashboard " ,
" admin.subtitle " : " Global overview of all non-archived servers and users. " ,
" admin.users " : " Users " ,
" admin.users_caption " : " Accounts " ,
" admin.servers " : " Servers " ,
" admin.servers_caption " : " not archived " ,
" admin.costs " : " Recurring costs " ,
" admin.per_month " : " per month " ,
" admin.contract_status " : " Contract status " ,
" admin.expiring " : " Expiring soon " ,
" admin.expired " : " Expired " ,
" admin.by_provider " : " By provider " ,
" admin.provider " : " Provider " ,
" admin.count " : " Servers " ,
" admin.monthly_costs " : " Monthly costs " ,
" admin.expiring_soon " : " Expiring soon " ,
" admin.expired_count " : " Expired " ,
" admin.contracts_expiring " : " Expiring soon (≤ 30 days) " ,
" admin.contracts_none_soon " : " No contracts expiring in the next 30 days. " ,
" admin.contracts_expired " : " Expired contracts " ,
" admin.contracts_none_expired " : " No expired contracts found. " ,
" users.title " : " User management " ,
" users.subtitle " : " Admins can activate/deactivate users. You cannot deactivate your own account. " ,
" users.username " : " Username " ,
" users.email " : " Email " ,
" users.role " : " Role " ,
" users.status " : " Status " ,
" users.action " : " Action " ,
" role.admin " : " Admin " ,
" role.user " : " User " ,
" status.active " : " active " ,
" status.inactive " : " deactivated " ,
" action.deactivate " : " Deactivate " ,
" action.activate " : " Activate " ,
" users.own_account " : " Own account " ,
" login.title " : " Login " ,
" login.subtitle " : " Sign in to manage your servers. " ,
" login.username " : " Username " ,
" login.password " : " Password " ,
" login.no_account " : " No account yet? " ,
" login.register " : " Register " ,
" login.submit " : " Log in " ,
" register.title " : " Register " ,
" register.subtitle " : " Create a new account. The first user becomes admin automatically. " ,
" register.username " : " Username " ,
" register.email " : " Email (optional) " ,
" register.password " : " Password " ,
" register.password_confirm " : " Confirm password " ,
" register.submit " : " Create account " ,
} ,
}
def resolve_locale ( request ) - > str :
""" Detect locale from session override or Accept-Language; default to de. """
2025-12-06 14:01:30 +00:00
try :
if " session " in request . scope :
session_lang = request . session . get ( " lang " )
if session_lang in AVAILABLE_LANGUAGES :
return session_lang
except Exception :
pass
2025-12-06 13:58:46 +00:00
accept = request . headers . get ( " accept-language " , " " )
for part in accept . split ( " , " ) :
code = part . split ( " ; " ) [ 0 ] . strip ( ) . lower ( )
if code . startswith ( " de " ) :
return " de "
if code . startswith ( " en " ) :
return " en "
return " de "
def translate ( key : str , locale : str = " de " , * * kwargs ) - > str :
""" Return translated string for a key. """
text = translations . get ( locale , { } ) . get (
key , translations [ " en " ] . get ( key , key )
)
try :
return text . format ( * * kwargs )
except Exception :
return text