fix(i18n): hardkodierte Strings in Cluster/Users/Backups/Alerts bereinigen
- Cluster: statusTag() bekommt t-Parameter; Status-Labels (online/offline/
joining/leaving/unknown) → cluster.status.* Keys
- Users: 'You'-Tag → t('users.you') / de: 'Ich'
- Backups: 'OK'-Status-Tag → t('backups.okTag')
- Alerts: Kanal-Kind-Labels + alle SMTP-Formular-Labels in t() überführt
- en/de: cluster.status.*, users.you, backups.okTag, alerts.smtp.*,
alerts.kindWebhook/kindEmail ergänzt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -60,7 +60,7 @@ import (
|
|||||||
usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users"
|
usersvc "git.netcell-it.de/projekte/edgeguard-native/internal/services/users"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.1.84"
|
var version = "1.1.85"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
addr := os.Getenv("EDGEGUARD_API_ADDR")
|
addr := os.Getenv("EDGEGUARD_API_ADDR")
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/setup"
|
"git.netcell-it.de/projekte/edgeguard-native/internal/services/setup"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.1.84"
|
var version = "1.1.85"
|
||||||
|
|
||||||
const usage = `edgeguard-ctl — EdgeGuard CLI
|
const usage = `edgeguard-ctl — EdgeGuard CLI
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import (
|
|||||||
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
|
"git.netcell-it.de/projekte/edgeguard-native/internal/services/tlscerts"
|
||||||
)
|
)
|
||||||
|
|
||||||
var version = "1.1.84"
|
var version = "1.1.85"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// renewTickInterval — how often we re-evaluate expiring certs.
|
// renewTickInterval — how often we re-evaluate expiring certs.
|
||||||
|
|||||||
@@ -454,7 +454,14 @@
|
|||||||
"joinTokenOneShot": "Einmalig anzeigen, einmalig nutzbar",
|
"joinTokenOneShot": "Einmalig anzeigen, einmalig nutzbar",
|
||||||
"joinTokenOneShotDesc": "Token jetzt sicher übertragen — er erscheint nie wieder. Gültig bis {{expires}}. Beim Einlösen via cluster-join wird er server-seitig als verbraucht markiert.",
|
"joinTokenOneShotDesc": "Token jetzt sicher übertragen — er erscheint nie wieder. Gültig bis {{expires}}. Beim Einlösen via cluster-join wird er server-seitig als verbraucht markiert.",
|
||||||
"joinTokenFailed": "Token-Generierung fehlgeschlagen",
|
"joinTokenFailed": "Token-Generierung fehlgeschlagen",
|
||||||
"joinCmdLabel": "Auf dem neuen Node ausführen:"
|
"joinCmdLabel": "Auf dem neuen Node ausführen:",
|
||||||
|
"status": {
|
||||||
|
"online": "online",
|
||||||
|
"offline": "offline",
|
||||||
|
"joining": "joining",
|
||||||
|
"leaving": "leaving",
|
||||||
|
"unknown": "unbekannt"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"ssl": {
|
"ssl": {
|
||||||
"title": "SSL-Zertifikate",
|
"title": "SSL-Zertifikate",
|
||||||
@@ -1048,6 +1055,16 @@
|
|||||||
"emptyEventsDesc": "Triggers (Cert-Expiry, Backup-Fail, Cluster-Drift, License-Invalid, etc.) haben noch keinen Event gefeuert. Wenn sie feuern, landen sie hier und werden an die konfigurierten Channels zugestellt.",
|
"emptyEventsDesc": "Triggers (Cert-Expiry, Backup-Fail, Cluster-Drift, License-Invalid, etc.) haben noch keinen Event gefeuert. Wenn sie feuern, landen sie hier und werden an die konfigurierten Channels zugestellt.",
|
||||||
"noChannels": "kein Channel aktiv",
|
"noChannels": "kein Channel aktiv",
|
||||||
"confirmDelete": "Channel {{name}} wirklich löschen?",
|
"confirmDelete": "Channel {{name}} wirklich löschen?",
|
||||||
|
"kindWebhook": "Webhook (Slack/Discord/Teams/HTTP-Endpoint)",
|
||||||
|
"kindEmail": "E-Mail (SMTP)",
|
||||||
|
"smtp": {
|
||||||
|
"host": "SMTP-Host",
|
||||||
|
"port": "SMTP-Port",
|
||||||
|
"from": "Absender (From)",
|
||||||
|
"username": "Benutzername",
|
||||||
|
"password": "Passwort",
|
||||||
|
"starttls": "STARTTLS"
|
||||||
|
},
|
||||||
"col": {
|
"col": {
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"kind": "Typ",
|
"kind": "Typ",
|
||||||
@@ -1132,6 +1149,7 @@
|
|||||||
"confirmDelete": "Backup {{file}} wirklich löschen?",
|
"confirmDelete": "Backup {{file}} wirklich löschen?",
|
||||||
"empty": "Noch keine Backups. Klicke „Backup jetzt erstellen\" oder warte den nächsten Auto-Tick ab.",
|
"empty": "Noch keine Backups. Klicke „Backup jetzt erstellen\" oder warte den nächsten Auto-Tick ab.",
|
||||||
"failedTag": "FEHLER",
|
"failedTag": "FEHLER",
|
||||||
|
"okTag": "OK",
|
||||||
"col": {
|
"col": {
|
||||||
"time": "Zeit",
|
"time": "Zeit",
|
||||||
"file": "Datei",
|
"file": "Datei",
|
||||||
@@ -1226,6 +1244,7 @@
|
|||||||
"emptyDesc": "Oben den ersten zusätzlichen Benutzer anlegen.",
|
"emptyDesc": "Oben den ersten zusätzlichen Benutzer anlegen.",
|
||||||
"errorEmailTaken": "Diese E-Mail-Adresse wird bereits verwendet.",
|
"errorEmailTaken": "Diese E-Mail-Adresse wird bereits verwendet.",
|
||||||
"cannotDeleteSelf": "Das eigene Konto kann nicht gelöscht werden.",
|
"cannotDeleteSelf": "Das eigene Konto kann nicht gelöscht werden.",
|
||||||
|
"you": "Ich"
|
||||||
"never": "Nie"
|
"never": "Nie"
|
||||||
},
|
},
|
||||||
"audit": {
|
"audit": {
|
||||||
|
|||||||
@@ -454,7 +454,14 @@
|
|||||||
"joinTokenOneShot": "Shown once, usable once",
|
"joinTokenOneShot": "Shown once, usable once",
|
||||||
"joinTokenOneShotDesc": "Transfer the token securely now — it won't be shown again. Valid until {{expires}}. Server marks it as consumed when redeemed via cluster-join.",
|
"joinTokenOneShotDesc": "Transfer the token securely now — it won't be shown again. Valid until {{expires}}. Server marks it as consumed when redeemed via cluster-join.",
|
||||||
"joinTokenFailed": "Token generation failed",
|
"joinTokenFailed": "Token generation failed",
|
||||||
"joinCmdLabel": "Run on the new node:"
|
"joinCmdLabel": "Run on the new node:",
|
||||||
|
"status": {
|
||||||
|
"online": "online",
|
||||||
|
"offline": "offline",
|
||||||
|
"joining": "joining",
|
||||||
|
"leaving": "leaving",
|
||||||
|
"unknown": "unknown"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"ssl": {
|
"ssl": {
|
||||||
"title": "SSL certificates",
|
"title": "SSL certificates",
|
||||||
@@ -1048,6 +1055,16 @@
|
|||||||
"emptyEventsDesc": "Triggers (cert expiry, backup failure, cluster drift, license invalid, etc.) haven't fired any events yet. When they do, they land here and get delivered to the configured channels.",
|
"emptyEventsDesc": "Triggers (cert expiry, backup failure, cluster drift, license invalid, etc.) haven't fired any events yet. When they do, they land here and get delivered to the configured channels.",
|
||||||
"noChannels": "no active channel",
|
"noChannels": "no active channel",
|
||||||
"confirmDelete": "Really delete channel {{name}}?",
|
"confirmDelete": "Really delete channel {{name}}?",
|
||||||
|
"kindWebhook": "Webhook (Slack/Discord/Teams/HTTP endpoint)",
|
||||||
|
"kindEmail": "Email (SMTP)",
|
||||||
|
"smtp": {
|
||||||
|
"host": "SMTP host",
|
||||||
|
"port": "SMTP port",
|
||||||
|
"from": "From",
|
||||||
|
"username": "Username",
|
||||||
|
"password": "Password",
|
||||||
|
"starttls": "STARTTLS"
|
||||||
|
},
|
||||||
"col": {
|
"col": {
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"kind": "Kind",
|
"kind": "Kind",
|
||||||
@@ -1132,6 +1149,7 @@
|
|||||||
"confirmDelete": "Really delete backup {{file}}?",
|
"confirmDelete": "Really delete backup {{file}}?",
|
||||||
"empty": "No backups yet. Click “Run backup now” or wait for the next scheduled tick.",
|
"empty": "No backups yet. Click “Run backup now” or wait for the next scheduled tick.",
|
||||||
"failedTag": "FAILED",
|
"failedTag": "FAILED",
|
||||||
|
"okTag": "OK",
|
||||||
"col": {
|
"col": {
|
||||||
"time": "Time",
|
"time": "Time",
|
||||||
"file": "File",
|
"file": "File",
|
||||||
@@ -1226,6 +1244,7 @@
|
|||||||
"emptyDesc": "Add the first additional user above.",
|
"emptyDesc": "Add the first additional user above.",
|
||||||
"errorEmailTaken": "This email address is already in use.",
|
"errorEmailTaken": "This email address is already in use.",
|
||||||
"cannotDeleteSelf": "You cannot delete your own account.",
|
"cannotDeleteSelf": "You cannot delete your own account.",
|
||||||
|
"you": "You",
|
||||||
"never": "Never"
|
"never": "Never"
|
||||||
},
|
},
|
||||||
"audit": {
|
"audit": {
|
||||||
|
|||||||
@@ -342,8 +342,8 @@ export default function AlertsPage() {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t('alerts.col.kind')} name="kind" rules={[{ required: true }]}>
|
<Form.Item label={t('alerts.col.kind')} name="kind" rules={[{ required: true }]}>
|
||||||
<Select options={[
|
<Select options={[
|
||||||
{ value: 'webhook', label: 'Webhook (Slack/Discord/Teams/HTTP-Endpoint)' },
|
{ value: 'webhook', label: t('alerts.kindWebhook') },
|
||||||
{ value: 'email', label: 'Email (SMTP)' },
|
{ value: 'email', label: t('alerts.kindEmail') },
|
||||||
]} />
|
]} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={kind === 'email' ? t('alerts.col.targetEmail') : t('alerts.col.targetWebhook')}
|
<Form.Item label={kind === 'email' ? t('alerts.col.targetEmail') : t('alerts.col.targetWebhook')}
|
||||||
@@ -353,22 +353,22 @@ export default function AlertsPage() {
|
|||||||
|
|
||||||
{kind === 'email' && (
|
{kind === 'email' && (
|
||||||
<>
|
<>
|
||||||
<Form.Item label="SMTP-Host" name="smtp_host" rules={[{ required: true }]}>
|
<Form.Item label={t('alerts.smtp.host')} name="smtp_host" rules={[{ required: true }]}>
|
||||||
<Input placeholder="smtp.gmail.com" />
|
<Input placeholder="smtp.gmail.com" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="SMTP-Port" name="smtp_port" rules={[{ required: true }]}>
|
<Form.Item label={t('alerts.smtp.port')} name="smtp_port" rules={[{ required: true }]}>
|
||||||
<InputNumber min={1} max={65535} style={{ width: '100%' }} />
|
<InputNumber min={1} max={65535} style={{ width: '100%' }} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="From" name="from" rules={[{ required: true }]}>
|
<Form.Item label={t('alerts.smtp.from')} name="from" rules={[{ required: true }]}>
|
||||||
<Input placeholder="edgeguard@your-domain.tld" />
|
<Input placeholder="edgeguard@your-domain.tld" />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Username" name="username">
|
<Form.Item label={t('alerts.smtp.username')} name="username">
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="Password" name="password">
|
<Form.Item label={t('alerts.smtp.password')} name="password">
|
||||||
<Input.Password />
|
<Input.Password />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="STARTTLS" name="use_tls" valuePropName="checked">
|
<Form.Item label={t('alerts.smtp.starttls')} name="use_tls" valuePropName="checked">
|
||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ export default function HistoryTab() {
|
|||||||
title: t('backups.col.status'), dataIndex: 'status', width: 110,
|
title: t('backups.col.status'), dataIndex: 'status', width: 110,
|
||||||
render: (s: Backup['status'], row) =>
|
render: (s: Backup['status'], row) =>
|
||||||
s === 'success'
|
s === 'success'
|
||||||
? <Tag color="green">OK</Tag>
|
? <Tag color="green">{t('backups.okTag')}</Tag>
|
||||||
: (
|
: (
|
||||||
<Tooltip title={row.error}>
|
<Tooltip title={row.error}>
|
||||||
<Tag color="red">{t('backups.failedTag')}</Tag>
|
<Tag color="red">{t('backups.failedTag')}</Tag>
|
||||||
|
|||||||
@@ -82,13 +82,13 @@ interface CertStatus {
|
|||||||
peer?: CertInfo
|
peer?: CertInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
function statusTag(s: HANode['status']) {
|
function statusTag(s: HANode['status'], t: (k: string) => string) {
|
||||||
switch (s) {
|
switch (s) {
|
||||||
case 'online': return <Tag color="green">online</Tag>
|
case 'online': return <Tag color="green">{t('cluster.status.online')}</Tag>
|
||||||
case 'offline': return <Tag color="red">offline</Tag>
|
case 'offline': return <Tag color="red">{t('cluster.status.offline')}</Tag>
|
||||||
case 'joining': return <Tag color="blue">joining</Tag>
|
case 'joining': return <Tag color="blue">{t('cluster.status.joining')}</Tag>
|
||||||
case 'leaving': return <Tag color="orange">leaving</Tag>
|
case 'leaving': return <Tag color="orange">{t('cluster.status.leaving')}</Tag>
|
||||||
default: return <Tag>unknown</Tag>
|
default: return <Tag>{t('cluster.status.unknown')}</Tag>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +208,7 @@ export default function ClusterPage() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: t('cluster.col.status'), dataIndex: 'status', width: 110,
|
title: t('cluster.col.status'), dataIndex: 'status', width: 110,
|
||||||
render: (s: HANode['status']) => statusTag(s),
|
render: (s: HANode['status']) => statusTag(s, t),
|
||||||
},
|
},
|
||||||
{ title: t('cluster.col.role'), dataIndex: 'role', width: 110,
|
{ title: t('cluster.col.role'), dataIndex: 'role', width: 110,
|
||||||
render: (v: string) => <Tag color={v === 'primary' ? 'gold' : 'default'}>{v}</Tag> },
|
render: (v: string) => <Tag color={v === 'primary' ? 'gold' : 'default'}>{v}</Tag> },
|
||||||
@@ -324,7 +324,7 @@ export default function ClusterPage() {
|
|||||||
</Space>
|
</Space>
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label={t('cluster.col.status')}>
|
<Descriptions.Item label={t('cluster.col.status')}>
|
||||||
{statusTag(data.local_node.status)}
|
{statusTag(data.local_node.status, t)}
|
||||||
</Descriptions.Item>
|
</Descriptions.Item>
|
||||||
<Descriptions.Item label={t('cluster.col.role')}>
|
<Descriptions.Item label={t('cluster.col.role')}>
|
||||||
<Tag color={data.local_node.role === 'primary' ? 'gold' : 'default'}>
|
<Tag color={data.local_node.role === 'primary' ? 'gold' : 'default'}>
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ export default function UsersPage() {
|
|||||||
render: (s: string) => (
|
render: (s: string) => (
|
||||||
<Space size={6}>
|
<Space size={6}>
|
||||||
<span>{s}</span>
|
<span>{s}</span>
|
||||||
{me?.actor === s && <Tag style={{ fontSize: 11, padding: '0 4px' }}>You</Tag>}
|
{me?.actor === s && <Tag style={{ fontSize: 11, padding: '0 4px' }}>{t('users.you')}</Tag>}
|
||||||
</Space>
|
</Space>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user