From e18037375fdba882319d1eafcc9364bce2612577 Mon Sep 17 00:00:00 2001 From: Debian Date: Sun, 24 May 2026 12:02:27 +0200 Subject: [PATCH] =?UTF-8?q?fix(i18n):=20relative-time=20strings=20vollst?= =?UTF-8?q?=C3=A4ndig=20lokalisiert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit relativeTime() in Dashboard, relTime() in WireGuard Clients+Servers und CertExpiry() im Cluster-Page haben bisher hardcodiertes Deutsch („vor 5s", „abgelaufen vor X Tagen") gerendert, unabhängig von der gewählten UI-Sprache. Neu: gemeinsame i18n-Keys common.relTime.{Xs,Xm,Xh,Xd,never} in EN+DE, sowie cluster.{certDaysRemaining,certExpiredDaysAgo}. Alle Helper- Funktionen nehmen jetzt t() als Parameter und sind damit sprachunabhängig. Co-Authored-By: Claude Sonnet 4.6 --- VERSION | 2 +- cmd/edgeguard-api/main.go | 2 +- management-ui/src/i18n/locales/de/common.json | 11 ++++++++- management-ui/src/i18n/locales/en/common.json | 11 ++++++++- management-ui/src/pages/Cluster/index.tsx | 5 +++- management-ui/src/pages/Dashboard/index.tsx | 24 +++++++++---------- management-ui/src/pages/Wireguard/Clients.tsx | 14 +++++------ management-ui/src/pages/Wireguard/Servers.tsx | 14 +++++------ 8 files changed, 52 insertions(+), 31 deletions(-) diff --git a/VERSION b/VERSION index c24ccab..6d8bf8c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.75 +1.1.76 diff --git a/cmd/edgeguard-api/main.go b/cmd/edgeguard-api/main.go index 9f7b1a4..91416c5 100644 --- a/cmd/edgeguard-api/main.go +++ b/cmd/edgeguard-api/main.go @@ -60,7 +60,7 @@ import ( usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users" ) -var version = "1.1.75" +var version = "1.1.76" func main() { addr := os.Getenv("EDGEGUARD_API_ADDR") diff --git a/management-ui/src/i18n/locales/de/common.json b/management-ui/src/i18n/locales/de/common.json index edf668d..0f4a057 100644 --- a/management-ui/src/i18n/locales/de/common.json +++ b/management-ui/src/i18n/locales/de/common.json @@ -436,6 +436,8 @@ "renewSelfConfirm": "Peer-Cert mit lokaler CA neu signieren (1 Jahr)? edgeguard-api Restart danach erforderlich.", "certRenewedRestartHint": "Cert erneuert — bitte sudo systemctl restart edgeguard-api ausführen.", "certRenewFailed": "Cert-Erneuerung fehlgeschlagen", + "certDaysRemaining": "{{n}} Tage", + "certExpiredDaysAgo": "abgelaufen vor {{n}} Tagen", "removePeerBtn": "Entfernen", "removePeerConfirmTitle": "Peer wirklich aus dem Cluster entfernen?", "removePeerConfirmDesc": "{{fqdn}} wird aus ha_nodes gelöscht. Firewall-Renderer entfernt seine IP aus dem peer_ipv4-Set. Auf der Peer-Seite läuft edgeguard-api weiter; manuell stoppen + cluster-tls löschen für vollständigen Decommission.", @@ -910,7 +912,14 @@ "copy": "Kopieren", "copied": "Kopiert", "close": "Schließen", - "refresh": "Aktualisieren" + "refresh": "Aktualisieren", + "relTime": { + "Xs": "vor {{n}}s", + "Xm": "vor {{n}}m", + "Xh": "vor {{n}}h", + "Xd": "vor {{n}}d", + "never": "nie" + } }, "license": { "title": "Lizenz", diff --git a/management-ui/src/i18n/locales/en/common.json b/management-ui/src/i18n/locales/en/common.json index 86ce9cb..3c3eac3 100644 --- a/management-ui/src/i18n/locales/en/common.json +++ b/management-ui/src/i18n/locales/en/common.json @@ -436,6 +436,8 @@ "renewSelfConfirm": "Re-sign peer cert with the local CA (1 year)? edgeguard-api restart required after.", "certRenewedRestartHint": "Cert renewed — please run sudo systemctl restart edgeguard-api.", "certRenewFailed": "Cert renewal failed", + "certDaysRemaining": "{{n}} days", + "certExpiredDaysAgo": "expired {{n}} d ago", "removePeerBtn": "Remove", "removePeerConfirmTitle": "Really remove peer from the cluster?", "removePeerConfirmDesc": "{{fqdn}} is deleted from ha_nodes. Firewall renderer drops its IP from the peer_ipv4 set. On the peer side edgeguard-api keeps running; manually stop it + delete cluster-tls for a full decommission.", @@ -910,7 +912,14 @@ "copy": "Copy", "copied": "Copied", "close": "Close", - "refresh": "Refresh" + "refresh": "Refresh", + "relTime": { + "Xs": "{{n}}s ago", + "Xm": "{{n}}m ago", + "Xh": "{{n}}h ago", + "Xd": "{{n}}d ago", + "never": "never" + } }, "license": { "title": "License", diff --git a/management-ui/src/pages/Cluster/index.tsx b/management-ui/src/pages/Cluster/index.tsx index d49f712..a482c17 100644 --- a/management-ui/src/pages/Cluster/index.tsx +++ b/management-ui/src/pages/Cluster/index.tsx @@ -541,12 +541,15 @@ export default function ClusterPage() { // CertExpiry rendert " Tage" mit Farbcode: rot < 30, orange < 90, // grün sonst. Tooltip zeigt das absolute NotAfter-Datum. function CertExpiry({ days, until }: { days: number; until: string }) { + const { t } = useTranslation() let color: string | undefined if (days < 0) color = '#cf1322' // already expired else if (days < 30) color = '#cf1322' // critical else if (days < 90) color = '#d4651a' // warning else color = '#52c41a' // healthy - const label = days < 0 ? `abgelaufen vor ${-days} Tagen` : `${days} Tage` + const label = days < 0 + ? t('cluster.certExpiredDaysAgo', { n: -days }) + : t('cluster.certDaysRemaining', { n: days }) return ( diff --git a/management-ui/src/pages/Dashboard/index.tsx b/management-ui/src/pages/Dashboard/index.tsx index 7a5f4e7..1f4cb9f 100644 --- a/management-ui/src/pages/Dashboard/index.tsx +++ b/management-ui/src/pages/Dashboard/index.tsx @@ -131,13 +131,13 @@ function formatBytes(n: number): string { while (v >= 1024 && i < u.length - 1) { v /= 1024; i++ } return `${v.toFixed(1)} ${u[i]}` } -function relativeTime(unix: number): string { - if (!unix) return 'nie' +function relativeTime(unix: number, t: (k: string, v?: Record) => string): string { + if (!unix) return t('common.relTime.never') const sec = Math.max(0, Math.floor(Date.now() / 1000) - unix) - if (sec < 60) return `vor ${sec}s` - if (sec < 3600) return `vor ${Math.floor(sec / 60)}m` - if (sec < 86400) return `vor ${Math.floor(sec / 3600)}h` - return `vor ${Math.floor(sec / 86400)}d` + if (sec < 60) return t('common.relTime.Xs', { n: sec }) + if (sec < 3600) return t('common.relTime.Xm', { n: Math.floor(sec / 60) }) + if (sec < 86400) return t('common.relTime.Xh', { n: Math.floor(sec / 3600) }) + return t('common.relTime.Xd', { n: Math.floor(sec / 86400) }) } function formatUptime(sec: number): string { if (sec < 60) return `${sec}s` @@ -148,10 +148,10 @@ function formatUptime(sec: number): string { if (h > 0) return `${h}h ${m}m` return `${m}m` } -function relativeFromIso(iso: string): string { - const t = new Date(iso).getTime() - if (!t) return '' - return relativeTime(Math.floor(t / 1000)) +function relativeFromIso(iso: string, t: (k: string, v?: Record) => string): string { + const ts = new Date(iso).getTime() + if (!ts) return '' + return relativeTime(Math.floor(ts / 1000), t) } // ── Page ────────────────────────────────────────────────────── @@ -438,7 +438,7 @@ export default function DashboardPage() { {e.action} {e.actor} {e.subject && {e.subject}} - {relativeFromIso(e.created_at)} + {relativeFromIso(e.created_at, t)} ))} @@ -493,7 +493,7 @@ export default function DashboardPage() { {status.map(s => (
{s.endpoint && {s.endpoint} · } - {relativeTime(s.last_handshake_unix)} + {relativeTime(s.last_handshake_unix, t)} {' · ▼'}{formatBytes(s.transfer_rx)}{' · ▲'}{formatBytes(s.transfer_tx)}
))} diff --git a/management-ui/src/pages/Wireguard/Clients.tsx b/management-ui/src/pages/Wireguard/Clients.tsx index eac17c9..7d5d174 100644 --- a/management-ui/src/pages/Wireguard/Clients.tsx +++ b/management-ui/src/pages/Wireguard/Clients.tsx @@ -39,13 +39,13 @@ function fmtBytes(n: number): string { while (v >= 1024 && i < u.length - 1) { v /= 1024; i++ } return `${v.toFixed(1)} ${u[i]}` } -function relTime(unix: number, never: string): string { - if (!unix) return never +function relTime(unix: number, t: (k: string, v?: Record) => string): string { + if (!unix) return t('common.relTime.never') const sec = Math.max(0, Math.floor(Date.now() / 1000) - unix) - if (sec < 60) return `${sec}s ago` - if (sec < 3600) return `${Math.floor(sec / 60)}m ago` - if (sec < 86400) return `${Math.floor(sec / 3600)}h ago` - return `${Math.floor(sec / 86400)}d ago` + if (sec < 60) return t('common.relTime.Xs', { n: sec }) + if (sec < 3600) return t('common.relTime.Xm', { n: Math.floor(sec / 60) }) + if (sec < 86400) return t('common.relTime.Xh', { n: Math.floor(sec / 3600) }) + return t('common.relTime.Xd', { n: Math.floor(sec / 86400) }) } interface ClientForm { @@ -145,7 +145,7 @@ export default function ClientsTab() { {live && ( - {relTime(live.last_handshake_unix, t('wg.peer.never'))} + {relTime(live.last_handshake_unix, t)} )} diff --git a/management-ui/src/pages/Wireguard/Servers.tsx b/management-ui/src/pages/Wireguard/Servers.tsx index 12120b7..41cdfef 100644 --- a/management-ui/src/pages/Wireguard/Servers.tsx +++ b/management-ui/src/pages/Wireguard/Servers.tsx @@ -79,13 +79,13 @@ function fmtBytes(n: number): string { while (v >= 1024 && i < u.length - 1) { v /= 1024; i++ } return `${v.toFixed(1)} ${u[i]}` } -function relTime(unix: number, never: string): string { - if (!unix) return never +function relTime(unix: number, t: (k: string, v?: Record) => string): string { + if (!unix) return t('common.relTime.never') const sec = Math.max(0, Math.floor(Date.now() / 1000) - unix) - if (sec < 60) return `vor ${sec}s` - if (sec < 3600) return `vor ${Math.floor(sec / 60)}m` - if (sec < 86400) return `vor ${Math.floor(sec / 3600)}h` - return `vor ${Math.floor(sec / 86400)}d` + if (sec < 60) return t('common.relTime.Xs', { n: sec }) + if (sec < 3600) return t('common.relTime.Xm', { n: Math.floor(sec / 60) }) + if (sec < 86400) return t('common.relTime.Xh', { n: Math.floor(sec / 3600) }) + return t('common.relTime.Xd', { n: Math.floor(sec / 86400) }) } interface FwZoneLite { name: string; builtin: boolean } @@ -405,7 +405,7 @@ function PeerDrawer({ iface, onClose }: PeerDrawerProps) { inactiveLabel={t('wg.peer.offline')} /> - {live ? relTime(live.last_handshake_unix, t('wg.peer.never')) : t('wg.peer.never')} + {live ? relTime(live.last_handshake_unix, t) : t('common.relTime.never')} )