fix(i18n): RemoteTargets S3/SFTP-Labels + NTP-Interface-Labels vollständig lokalisieren

RemoteTargets: alle 20+ hardkodierten Form-Labels und deutschen Hilfetexte
(S3: Endpoint/Region/Bucket/AccessKey/SecretKey/PathPrefix/HTTPS;
 SFTP: Host/Port/Username/Password/PrivateKey/RemoteDir/HostKeyFp)
sind jetzt in t()-Aufrufen mit remotes.s3.*/remotes.sftp.*-Keys.

NTP: Listen-Address-Dropdown-Labels (alle IPv4/IPv6-Interfaces, Loopback)
verwenden jetzt dns.settings.allIPv4/allIPv6/loopback (identisch zu DNS-Settings).
NTP-Pool-Adresse-Placeholder → t('ntp.pool.addressPlaceholder').

en/de: alle neuen Keys ergänzt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-24 12:52:13 +02:00
parent f6852256b1
commit 49e3e39ea6
8 changed files with 91 additions and 33 deletions

View File

@@ -1 +1 @@
1.1.85 1.1.86

View File

@@ -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.85" var version = "1.1.86"
func main() { func main() {
addr := os.Getenv("EDGEGUARD_API_ADDR") addr := os.Getenv("EDGEGUARD_API_ADDR")

View File

@@ -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.85" var version = "1.1.86"
const usage = `edgeguard-ctl — EdgeGuard CLI const usage = `edgeguard-ctl — EdgeGuard CLI

View File

@@ -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.85" var version = "1.1.86"
const ( const (
// renewTickInterval — how often we re-evaluate expiring certs. // renewTickInterval — how often we re-evaluate expiring certs.

View File

@@ -806,6 +806,7 @@
"kindServer": "server — einzelner Host", "kindServer": "server — einzelner Host",
"address": "Adresse / Host", "address": "Adresse / Host",
"addressExtra": "FQDN (für pool: 0.de.pool.ntp.org) oder IP.", "addressExtra": "FQDN (für pool: 0.de.pool.ntp.org) oder IP.",
"addressPlaceholder": "0.de.pool.ntp.org oder 10.0.0.10",
"iburst": "iburst", "iburst": "iburst",
"prefer": "prefer", "prefer": "prefer",
"minpoll": "min-poll", "minpoll": "min-poll",
@@ -1090,6 +1091,34 @@
"testFailed": "Test fehlgeschlagen", "testFailed": "Test fehlgeschlagen",
"confirmDelete": "Ziel {{name}} wirklich löschen?", "confirmDelete": "Ziel {{name}} wirklich löschen?",
"targetExtra": "Frei-Text-Label für die Übersicht. Setze hier den Bucket/Path-Hinweis (s3://my-bucket oder sftp://backup@host).", "targetExtra": "Frei-Text-Label für die Übersicht. Setze hier den Bucket/Path-Hinweis (s3://my-bucket oder sftp://backup@host).",
"namePlaceholder": "z.B. MinIO offsite / Hetzner Storage Box",
"kindS3": "S3 (AWS / MinIO / R2 / B2 / Hetzner Object)",
"kindSftp": "SFTP (SSH)",
"s3": {
"endpoint": "Endpoint",
"endpointExtra": "s3.amazonaws.com / minio.example.com:9000 / fsn1.your-objectstorage.com",
"region": "Region",
"regionPlaceholder": "eu-central-1 / auto (R2)",
"bucket": "Bucket",
"accessKey": "Access Key",
"secretKey": "Secret Key",
"pathPrefix": "Path Prefix",
"pathPrefixExtra": "z.B. edgeguard/utm-1 — wird vor jedem Dateinamen gesetzt",
"useSsl": "HTTPS (use_ssl)"
},
"sftp": {
"host": "Host",
"port": "Port",
"username": "Benutzername",
"password": "Passwort",
"passwordExtra": "Entweder Passwort ODER Private Key.",
"privateKey": "Private Key (OpenSSH, base64)",
"privateKeyPlaceholder": "base64-kodierter OpenSSH Private Key",
"remoteDir": "Remote-Verzeichnis",
"remoteDirExtra": "z.B. /backups/edgeguard",
"hostKeyFp": "Host-Key-Fingerprint",
"hostKeyFpExtra": "Optional. SHA256:... — leer lassen für TOFU (unsicher gegen MitM)"
},
"col": { "col": {
"name": "Name", "name": "Name",
"kind": "Typ", "kind": "Typ",

View File

@@ -806,6 +806,7 @@
"kindServer": "server — single host", "kindServer": "server — single host",
"address": "Address / host", "address": "Address / host",
"addressExtra": "FQDN (for pool: 0.de.pool.ntp.org) or IP.", "addressExtra": "FQDN (for pool: 0.de.pool.ntp.org) or IP.",
"addressPlaceholder": "0.de.pool.ntp.org or 10.0.0.10",
"iburst": "iburst", "iburst": "iburst",
"prefer": "prefer", "prefer": "prefer",
"minpoll": "min-poll", "minpoll": "min-poll",
@@ -1090,6 +1091,34 @@
"testFailed": "Test failed", "testFailed": "Test failed",
"confirmDelete": "Really delete target {{name}}?", "confirmDelete": "Really delete target {{name}}?",
"targetExtra": "Free-text label for the overview. Use bucket/path hint (s3://my-bucket or sftp://backup@host).", "targetExtra": "Free-text label for the overview. Use bucket/path hint (s3://my-bucket or sftp://backup@host).",
"namePlaceholder": "e.g. MinIO offsite / Hetzner Storage Box",
"kindS3": "S3 (AWS / MinIO / R2 / B2 / Hetzner Object)",
"kindSftp": "SFTP (SSH)",
"s3": {
"endpoint": "Endpoint",
"endpointExtra": "s3.amazonaws.com / minio.example.com:9000 / fsn1.your-objectstorage.com",
"region": "Region",
"regionPlaceholder": "eu-central-1 / auto (R2)",
"bucket": "Bucket",
"accessKey": "Access Key",
"secretKey": "Secret Key",
"pathPrefix": "Path Prefix",
"pathPrefixExtra": "e.g. edgeguard/utm-1 — prepended to every filename",
"useSsl": "HTTPS (use_ssl)"
},
"sftp": {
"host": "Host",
"port": "Port",
"username": "Username",
"password": "Password",
"passwordExtra": "Either password OR private key.",
"privateKey": "Private Key (OpenSSH, base64)",
"privateKeyPlaceholder": "base64-encoded OpenSSH private key",
"remoteDir": "Remote Dir",
"remoteDirExtra": "e.g. /backups/edgeguard",
"hostKeyFp": "Host Key Fingerprint",
"hostKeyFpExtra": "Optional. SHA256:... — leave empty for TOFU (insecure against MitM)"
},
"col": { "col": {
"name": "Name", "name": "Name",
"kind": "Kind", "kind": "Kind",

View File

@@ -250,12 +250,12 @@ export default function RemoteTargetsTab() {
<Form form={form} layout="vertical" <Form form={form} layout="vertical"
onFinish={(v) => edit ? update.mutate({ id: edit.id, v }) : create.mutate(v)}> onFinish={(v) => edit ? update.mutate({ id: edit.id, v }) : create.mutate(v)}>
<Form.Item label={t('remotes.col.name')} name="name" rules={[{ required: true }]}> <Form.Item label={t('remotes.col.name')} name="name" rules={[{ required: true }]}>
<Input placeholder="MinIO offsite / Hetzner Storage Box" /> <Input placeholder={t('remotes.namePlaceholder')} />
</Form.Item> </Form.Item>
<Form.Item label={t('remotes.col.kind')} name="kind" rules={[{ required: true }]}> <Form.Item label={t('remotes.col.kind')} name="kind" rules={[{ required: true }]}>
<Select options={[ <Select options={[
{ value: 's3', label: 'S3 (AWS / MinIO / R2 / B2 / Hetzner Object)' }, { value: 's3', label: t('remotes.kindS3') },
{ value: 'sftp', label: 'SFTP (SSH)' }, { value: 'sftp', label: t('remotes.kindSftp') },
]} /> ]} />
</Form.Item> </Form.Item>
<Form.Item label={t('remotes.col.target')} name="target_url" rules={[{ required: true }]} <Form.Item label={t('remotes.col.target')} name="target_url" rules={[{ required: true }]}
@@ -265,27 +265,27 @@ export default function RemoteTargetsTab() {
{kind === 's3' && ( {kind === 's3' && (
<> <>
<Form.Item label="Endpoint" name="endpoint" rules={[{ required: true }]} <Form.Item label={t('remotes.s3.endpoint')} name="endpoint" rules={[{ required: true }]}
extra="s3.amazonaws.com / minio.example.com:9000 / fsn1.your-objectstorage.com"> extra={t('remotes.s3.endpointExtra')}>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item label="Region" name="region"> <Form.Item label={t('remotes.s3.region')} name="region">
<Input placeholder="eu-central-1 / auto (R2)" /> <Input placeholder={t('remotes.s3.regionPlaceholder')} />
</Form.Item> </Form.Item>
<Form.Item label="Bucket" name="bucket" rules={[{ required: true }]}> <Form.Item label={t('remotes.s3.bucket')} name="bucket" rules={[{ required: true }]}>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item label="Access Key" name="access_key" rules={[{ required: true }]}> <Form.Item label={t('remotes.s3.accessKey')} name="access_key" rules={[{ required: true }]}>
<Input autoComplete="off" /> <Input autoComplete="off" />
</Form.Item> </Form.Item>
<Form.Item label="Secret Key" name="secret_key" rules={[{ required: !edit }]}> <Form.Item label={t('remotes.s3.secretKey')} name="secret_key" rules={[{ required: !edit }]}>
<Input.Password autoComplete="new-password" /> <Input.Password autoComplete="new-password" />
</Form.Item> </Form.Item>
<Form.Item label="Path Prefix" name="path_prefix" <Form.Item label={t('remotes.s3.pathPrefix')} name="path_prefix"
extra="z.B. edgeguard/utm-1 — wird vor jedem Filename gesetzt"> extra={t('remotes.s3.pathPrefixExtra')}>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item label="HTTPS (use_ssl)" name="use_ssl" valuePropName="checked"> <Form.Item label={t('remotes.s3.useSsl')} name="use_ssl" valuePropName="checked">
<Switch /> <Switch />
</Form.Item> </Form.Item>
</> </>
@@ -293,29 +293,29 @@ export default function RemoteTargetsTab() {
{kind === 'sftp' && ( {kind === 'sftp' && (
<> <>
<Form.Item label="Host" name="host" rules={[{ required: true }]}> <Form.Item label={t('remotes.sftp.host')} name="host" rules={[{ required: true }]}>
<Input placeholder="backup.example.com" /> <Input placeholder="backup.example.com" />
</Form.Item> </Form.Item>
<Form.Item label="Port" name="port" rules={[{ required: true }]}> <Form.Item label={t('remotes.sftp.port')} name="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="Username" name="username" rules={[{ required: true }]}> <Form.Item label={t('remotes.sftp.username')} name="username" rules={[{ required: true }]}>
<Input autoComplete="off" /> <Input autoComplete="off" />
</Form.Item> </Form.Item>
<Form.Item label="Password" name="password" <Form.Item label={t('remotes.sftp.password')} name="password"
extra="Entweder Password ODER Private-Key."> extra={t('remotes.sftp.passwordExtra')}>
<Input.Password autoComplete="new-password" /> <Input.Password autoComplete="new-password" />
</Form.Item> </Form.Item>
<Form.Item label="Private Key (OpenSSH, base64)" name="private_key"> <Form.Item label={t('remotes.sftp.privateKey')} name="private_key">
<Input.TextArea rows={3} autoComplete="off" <Input.TextArea rows={3} autoComplete="off"
placeholder="base64-encoded OpenSSH private key" /> placeholder={t('remotes.sftp.privateKeyPlaceholder')} />
</Form.Item> </Form.Item>
<Form.Item label="Remote Dir" name="remote_dir" rules={[{ required: true }]} <Form.Item label={t('remotes.sftp.remoteDir')} name="remote_dir" rules={[{ required: true }]}
extra="z.B. /backups/edgeguard"> extra={t('remotes.sftp.remoteDirExtra')}>
<Input /> <Input />
</Form.Item> </Form.Item>
<Form.Item label="Host-Key-Fingerprint" name="host_key_fingerprint" <Form.Item label={t('remotes.sftp.hostKeyFp')} name="host_key_fingerprint"
extra="optional, SHA256:... — wenn leer wird TOFU verwendet (unsicher gegen MitM)"> extra={t('remotes.sftp.hostKeyFpExtra')}>
<Input placeholder="SHA256:abc123..." /> <Input placeholder="SHA256:abc123..." />
</Form.Item> </Form.Item>
</> </>

View File

@@ -303,7 +303,7 @@ function PoolsTab() {
</Form.Item> </Form.Item>
<Form.Item label={t('ntp.pool.address')} name="address" rules={[{ required: true }]} <Form.Item label={t('ntp.pool.address')} name="address" rules={[{ required: true }]}
extra={t('ntp.pool.addressExtra')}> extra={t('ntp.pool.addressExtra')}>
<Input placeholder="0.de.pool.ntp.org oder 10.0.0.10" /> <Input placeholder={t('ntp.pool.addressPlaceholder')} />
</Form.Item> </Form.Item>
<Space> <Space>
<Form.Item label={t('ntp.pool.iburst')} name="iburst" valuePropName="checked"> <Form.Item label={t('ntp.pool.iburst')} name="iburst" valuePropName="checked">
@@ -341,10 +341,10 @@ function SettingsTab() {
const [form] = Form.useForm<SettingsForm>() const [form] = Form.useForm<SettingsForm>()
const ipOptions: { value: string; label: string }[] = [ const ipOptions: { value: string; label: string }[] = [
{ value: '0.0.0.0', label: '0.0.0.0 — alle IPv4-Interfaces' }, { value: '0.0.0.0', label: `0.0.0.0 — ${t('dns.settings.allIPv4')}` },
{ value: '::', label: ':: — alle IPv6-Interfaces' }, { value: '::', label: `:: — ${t('dns.settings.allIPv6')}` },
{ value: '127.0.0.1', label: '127.0.0.1 — Loopback IPv4' }, { value: '127.0.0.1', label: `127.0.0.1 — ${t('dns.settings.loopback')} IPv4` },
{ value: '::1', label: '::1 — Loopback IPv6' }, { value: '::1', label: `::1 — ${t('dns.settings.loopback')} IPv6` },
] ]
for (const i of sys ?? []) { for (const i of sys ?? []) {
if (i.ifname === 'lo') continue if (i.ifname === 'lo') continue