From cc7c75ba333805c1a334e3205dde2de417349a16 Mon Sep 17 00:00:00 2001 From: nocci Date: Sat, 6 Dec 2025 13:58:46 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=8C=90=20i18n(i18n):=20add=20multilingual?= =?UTF-8?q?=20support=20with=20translations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- app/i18n.py | 350 +++++++++++++++++++++++++++++ app/main.py | 32 +++ app/templates/admin_dashboard.html | 42 ++-- app/templates/base.html | 37 +-- app/templates/login.html | 12 +- app/templates/register.html | 14 +- app/templates/server_detail.html | 70 +++--- app/templates/server_form.html | 70 +++--- app/templates/servers_list.html | 46 ++-- app/templates/servers_map.html | 8 +- app/templates/users_list.html | 24 +- 11 files changed, 548 insertions(+), 157 deletions(-) create mode 100644 app/i18n.py diff --git a/app/i18n.py b/app/i18n.py new file mode 100644 index 0000000..0e7ea71 --- /dev/null +++ b/app/i18n.py @@ -0,0 +1,350 @@ +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 diff --git a/app/main.py b/app/main.py index 6ecb272..1708921 100644 --- a/app/main.py +++ b/app/main.py @@ -18,6 +18,9 @@ from .utils import ( ensure_csrf_token, validate_csrf, ) +from jinja2 import pass_context + +from .i18n import AVAILABLE_LANGUAGES, resolve_locale, translate from .auth import ( hash_password, verify_password, @@ -30,6 +33,7 @@ app = FastAPI(title="FleetLedger") app.mount("/static", StaticFiles(directory="app/static"), name="static") templates = Jinja2Templates(directory="app/templates") +templates.env.globals["languages"] = AVAILABLE_LANGUAGES # Session middleware (server-side session based on signed cookie) SESSION_SECRET = os.getenv("SESSION_SECRET") @@ -50,6 +54,23 @@ app.add_middleware( ) +@app.middleware("http") +async def add_locale_to_request(request: Request, call_next): + request.state.locale = resolve_locale(request) + response = await call_next(request) + return response + + +@pass_context +def _t(ctx, key: str, **kwargs) -> str: + request = ctx.get("request") + locale = getattr(request.state, "locale", "de") if request else "de" + return translate(key, locale, **kwargs) + + +templates.env.globals["t"] = _t + + @app.on_event("startup") def on_startup() -> None: """Initialize database on startup.""" @@ -74,6 +95,17 @@ def service_worker() -> FileResponse: ) +@app.get("/lang/{code}", include_in_schema=False) +def switch_language(code: str, request: Request): + """Persist preferred language in session and redirect back.""" + code = code.lower() + if code not in AVAILABLE_LANGUAGES: + return RedirectResponse("/", status_code=303) + request.session["lang"] = code + referer = request.headers.get("referer") or "/" + return RedirectResponse(referer, status_code=303) + + # ------------- Auth: Register / Login / Logout ------------- diff --git a/app/templates/admin_dashboard.html b/app/templates/admin_dashboard.html index 18394db..0e19340 100644 --- a/app/templates/admin_dashboard.html +++ b/app/templates/admin_dashboard.html @@ -3,9 +3,9 @@
-

Admin-Dashboard

+

{{ t("admin.title") }}

- Globale Übersicht über alle nicht archivierten Server und Benutzer. + {{ t("admin.subtitle") }}

@@ -13,36 +13,36 @@ {% if stats %}
- Benutzer + {{ t("admin.users") }} {{ stats.total_users }} - Accounts + {{ t("admin.users_caption") }}
- Server + {{ t("admin.servers") }} {{ stats.total_servers }} - nicht archiviert + {{ t("admin.servers_caption") }}
- Laufende Kosten + {{ t("admin.costs") }} {{ "%.2f"|format(stats.monthly_total) }} {% if stats.monthly_currency %} {{ stats.monthly_currency }}{% endif %} - pro Monat{% if stats.mixed_currencies %} (gemischte Währungen){% endif %} + {{ t("admin.per_month") }}{% if stats.mixed_currencies %} {{ t("stats.mixed_currencies") }}{% endif %}
- Vertragstatus + {{ t("admin.contract_status") }} - Bald auslaufend: {{ stats.expiring_soon }} + {{ t("admin.expiring") }}: {{ stats.expiring_soon }} - Abgelaufen: {{ stats.expired }} + {{ t("admin.expired") }}: {{ stats.expired }}
@@ -50,16 +50,16 @@
-

Nach Provider

+

{{ t("admin.by_provider") }}

- - - - - + + + + + @@ -95,7 +95,7 @@
-

Laufen bald aus (≤ 30 Tage)

+

{{ t("admin.contracts_expiring") }}

{% if expiring_soon_list %}
    @@ -117,13 +117,13 @@ {% endfor %}
{% else %} -
Keine Verträge laufen in den nächsten 30 Tagen aus.
+
{{ t("admin.contracts_none_soon") }}
{% endif %}
-

Abgelaufene Verträge

+

{{ t("admin.contracts_expired") }}

{% if expired_list %}
    @@ -145,7 +145,7 @@ {% endfor %}
{% else %} -
Keine abgelaufenen Verträge gefunden.
+
{{ t("admin.contracts_none_expired") }}
{% endif %}
diff --git a/app/templates/base.html b/app/templates/base.html index ded4c28..fdd67c4 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -1,5 +1,5 @@ - + FleetLedger @@ -37,52 +37,63 @@
FleetLedger
-
Deine gemieteten Server im Blick
+
{{ t("brand.tagline") }}
{% if current_user %} - Eingeloggt als {{ current_user.username }} + {{ t("nav.logged_in_as") }} {{ current_user.username }} {% if current_user.is_admin %} Admin {% endif %} {% if current_user.is_admin %} - Admin-Dashboard + {{ t("nav.admin") }} - User + {{ t("nav.users") }} {% endif %} - Übersicht + {{ t("nav.overview") }} - Karte + {{ t("nav.map") }} - Server anlegen + {{ t("nav.new_server") }} - Logout + {{ t("nav.logout") }} {% else %} - Login + {{ t("nav.login") }} - Registrieren + {{ t("nav.register") }} {% endif %} +
+ {% for code, label in languages.items() %} + + {{ label }} + + {% if not loop.last %}|{% endif %} + {% endfor %} +
diff --git a/app/templates/login.html b/app/templates/login.html index 70dce5c..3e7fa39 100644 --- a/app/templates/login.html +++ b/app/templates/login.html @@ -1,8 +1,8 @@ {% extends "base.html" %} {% block content %}
-

Login

-

Melde dich an, um deine Server zu verwalten.

+

{{ t("login.title") }}

+

{{ t("login.subtitle") }}

{% if error %}
@@ -13,7 +13,7 @@
- +
- +
- Noch kein Account? Registrieren + {{ t("login.no_account") }} {{ t("login.register") }}
diff --git a/app/templates/register.html b/app/templates/register.html index 09df005..d76e807 100644 --- a/app/templates/register.html +++ b/app/templates/register.html @@ -1,9 +1,9 @@ {% extends "base.html" %} {% block content %}
-

Registrieren

+

{{ t("register.title") }}

- Erstelle einen neuen Account. Der erste Benutzer wird automatisch Admin. + {{ t("register.subtitle") }}

{% if error %} @@ -15,7 +15,7 @@
- +
- +
- +
- + - Account anlegen + {{ t("register.submit") }}
diff --git a/app/templates/server_detail.html b/app/templates/server_detail.html index 70fa106..8c4a6d0 100644 --- a/app/templates/server_detail.html +++ b/app/templates/server_detail.html @@ -3,16 +3,16 @@
-
Server
+
{{ t("server_detail.server") }}

{{ server.name }} {% if server.is_expired %} - abgelaufen + {{ t("status.expired") }} {% elif server.is_expiring_soon %} - läuft bald aus + {{ t("status.expiring") }} {% endif %}

@@ -21,14 +21,14 @@ {% endif %} {% if server.contract_end %}

- Vertragsende: {{ server.contract_end }} + {{ t("server_detail.contract_end") }}: {{ server.contract_end }} {% if server.days_until_contract_end is not none %} {% if server.days_until_contract_end < 0 %} - (vor {{ (server.days_until_contract_end * -1) }} Tagen) + ({{ t("server_detail.days_ago", days=(server.days_until_contract_end * -1)) }}) {% elif server.days_until_contract_end == 0 %} - (heute) + ({{ t("server_detail.today") }}) {% else %} - (in {{ server.days_until_contract_end }} Tagen) + ({{ t("server_detail.in_days", days=server.days_until_contract_end) }}) {% endif %} {% endif %}

@@ -55,15 +55,15 @@ href="/servers/{{ server.id }}/edit" class="ml-2 rounded-lg border border-slate-700 px-3 py-1 text-xs text-slate-100 hover:border-slate-500 hover:text-white" > - Bearbeiten + {{ t("server_detail.edit") }} -
+
@@ -72,30 +72,30 @@
-

Netz & Kosten

+

{{ t("section.net_costs") }}

-
IPv4
+
{{ t("field.ipv4") }}
{% if server.ipv4 %}{{ server.ipv4 }}{% else %}–{% endif %}
-
IPv6
+
{{ t("field.ipv6") }}
{% if server.ipv6 %}{{ server.ipv6 }}{% else %}–{% endif %}
-
Kosten
+
{{ t("field.costs") }}
{% if server.price %} {{ "%.2f"|format(server.price) }} {{ server.currency }} / - {{ "Monat" if server.billing_period == "monthly" else "Jahr" }} + {{ t("price.month") if server.billing_period == "monthly" else t("price.year") }} {% else %}–{% endif %}
-
Vertrag
+
{{ t("field.contract") }}
- {% if server.contract_start %}ab {{ server.contract_start }}{% endif %} - {% if server.contract_end %} bis {{ server.contract_end }}{% endif %} + {% if server.contract_start %}{{ t("label.contract_start") }} {{ server.contract_start }}{% endif %} + {% if server.contract_end %} {{ t("label.contract_end") }} {{ server.contract_end }}{% endif %} {% if not server.contract_start and not server.contract_end %}–{% endif %}
@@ -104,23 +104,23 @@
-

Hardware

+

{{ t("section.hardware") }}

-
CPU
+
{{ t("field.cpu") }}
{% if server.cpu_model %}{{ server.cpu_model }}{% else %}–{% endif %} {% if server.cpu_cores %} ({{ server.cpu_cores }} Cores){% endif %}
-
RAM
+
{{ t("field.ram") }}
{% if server.ram_mb %}{{ server.ram_mb }} MB{% else %}–{% endif %}
-
Storage
+
{{ t("field.storage") }}
{% if server.storage_gb %}{{ server.storage_gb }} GB{% else %}–{% endif %} {% if server.storage_type %} ({{ server.storage_type }}){% endif %} @@ -131,30 +131,30 @@
-

Zugänge

+

{{ t("section.access") }}

-
Management
+
{{ t("field.management") }}
{% if server.mgmt_url %} - Console öffnen + {{ t("nav.console") }} {% else %}–{% endif %}
-
Mgmt-User
+
{{ t("field.mgmt_user") }}
{% if server.mgmt_user %}{{ server.mgmt_user }}{% else %}–{% endif %}
-
Mgmt-Passwort
+
{{ t("field.mgmt_password") }}
{% if server.mgmt_password_encrypted %} {% if mgmt_password %} {{ mgmt_password }} {% else %} - verschlüsselt gespeichert (Key fehlt?) + {{ t("mgmt.password_encrypted_missing") }} {% endif %} {% else %} – @@ -162,11 +162,11 @@
-
SSH User
+
{{ t("field.ssh_user") }}
{% if server.ssh_user %}{{ server.ssh_user }}{% else %}–{% endif %}
-
SSH Key Hint
+
{{ t("field.ssh_key_hint") }}
{% if server.ssh_key_hint %} {{ server.ssh_key_hint }} @@ -175,22 +175,22 @@

- Hinweis: Es werden nur Key-Namen gespeichert, keine privaten SSH-Schlüssel. + {{ t("note.ssh_keys") }}

-

Notizen

+

{{ t("section.notes") }}

- {% if server.notes %}{{ server.notes }}{% else %}Keine Notizen.{% endif %} + {% if server.notes %}{{ server.notes }}{% else %}{{ t("notes.empty") }}{% endif %}
- Zurück zur Übersicht - Zuletzt aktualisiert: {{ server.updated_at }} + {{ t("back.overview") }} + {{ t("updated_at") }} {{ server.updated_at }}
{% endblock %} diff --git a/app/templates/server_form.html b/app/templates/server_form.html index 13a4c9e..a258091 100644 --- a/app/templates/server_form.html +++ b/app/templates/server_form.html @@ -2,15 +2,15 @@ {% block content %}

- {% if server %}Server bearbeiten{% else %}Neuen Server anlegen{% endif %} + {% if server %}{{ t("form.title.edit") }}{% else %}{{ t("form.title.new") }}{% endif %}

- Trage alle relevanten Infos zu deinem VPS / Server ein. + {{ t("form.subtitle") }}

{% if not can_encrypt %}
- ENCRYPTION_KEY ist nicht gesetzt – eingegebene Management-Passwörter werden nicht gespeichert. + {{ t("warning.no_encryption_short") }}
{% endif %} @@ -18,10 +18,10 @@
-

Allgemein

+

{{ t("section.general") }}

- +
- +
- +
- +
- +
-

Netzwerk

+

{{ t("section.network") }}

- +
- +
-

Kosten

+

{{ t("section.costs") }}

- +
- +
- +
- +
-

Hardware

+

{{ t("section.hardware.small") }}

- +
- +
- +
- +
- +
-

Zugänge

+

{{ t("section.access.small") }}

- SSH: hier nur Key-Namen oder Hints eintragen, keine privaten Keys. + {{ t("hint.ssh") }}

- +
- +
- +
- +
- +
-

Notizen

+

{{ t("section.notes.small") }}

ProviderServerMonatskostenLaufen bald ausAbgelaufen{{ t("admin.provider") }}{{ t("admin.count") }}{{ t("admin.monthly_costs") }}{{ t("admin.expiring_soon") }}{{ t("admin.expired_count") }}