server-verwaltung/app/i18n.py
nocci cc7c75ba33 🌐 i18n(i18n): add multilingual support with translations
- create i18n.py for managing translations and resolving locale
- add German and English translations for various UI components
- integrate translation functions into templates for dynamic language support
2025-12-06 13:58:46 +00:00

350 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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",
"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",
"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",
"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",
"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."""
session_lang = request.session.get("lang")
if session_lang in AVAILABLE_LANGUAGES:
return session_lang
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