feat(i18n): add German (de) locale (#28495)

Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: e418326aaf
Co-authored-by: dsantoreis <220753637+dsantoreis@users.noreply.github.com>
Co-authored-by: Evizero <10854026+Evizero@users.noreply.github.com>
Reviewed-by: @Evizero
This commit is contained in:
Daniel Reis
2026-02-27 11:44:09 +01:00
committed by GitHub
parent 6b317b1f17
commit 84a88b2ace
9 changed files with 143 additions and 7 deletions

View File

@@ -3,7 +3,7 @@ import type { Locale, TranslationMap } from "./types.ts";
type Subscriber = (locale: Locale) => void;
export const SUPPORTED_LOCALES: ReadonlyArray<Locale> = ["en", "zh-CN", "zh-TW", "pt-BR"];
export const SUPPORTED_LOCALES: ReadonlyArray<Locale> = ["en", "zh-CN", "zh-TW", "pt-BR", "de"];
export function isSupportedLocale(value: string | null | undefined): value is Locale {
return value !== null && value !== undefined && SUPPORTED_LOCALES.includes(value as Locale);
@@ -30,6 +30,9 @@ class I18nManager {
if (navLang.startsWith("pt")) {
return "pt-BR";
}
if (navLang.startsWith("de")) {
return "de";
}
return "en";
}
@@ -64,6 +67,8 @@ class I18nManager {
module = await import("../locales/zh-TW.ts");
} else if (locale === "pt-BR") {
module = await import("../locales/pt-BR.ts");
} else if (locale === "de") {
module = await import("../locales/de.ts");
} else {
return;
}

View File

@@ -1,6 +1,6 @@
export type TranslationMap = { [key: string]: string | TranslationMap };
export type Locale = "en" | "zh-CN" | "zh-TW" | "pt-BR";
export type Locale = "en" | "zh-CN" | "zh-TW" | "pt-BR" | "de";
export interface I18nConfig {
locale: Locale;

126
ui/src/i18n/locales/de.ts Normal file
View File

@@ -0,0 +1,126 @@
import type { TranslationMap } from "../lib/types.ts";
export const de: TranslationMap = {
common: {
version: "Version",
health: "Status",
ok: "OK",
offline: "Offline",
connect: "Verbinden",
refresh: "Aktualisieren",
enabled: "Aktiviert",
disabled: "Deaktiviert",
na: "k. A.",
docs: "Dokumentation",
resources: "Ressourcen",
},
nav: {
chat: "Chat",
control: "Steuerung",
agent: "Agent",
settings: "Einstellungen",
expand: "Seitenleiste ausklappen",
collapse: "Seitenleiste einklappen",
},
tabs: {
agents: "Agenten",
overview: "Übersicht",
channels: "Kanäle",
instances: "Instanzen",
sessions: "Sitzungen",
usage: "Nutzung",
cron: "Cron-Aufgaben",
skills: "Fähigkeiten",
nodes: "Geräte",
chat: "Chat",
config: "Konfiguration",
debug: "Debug",
logs: "Protokolle",
},
subtitles: {
agents: "Agent-Arbeitsbereiche, Tools und Identitäten verwalten.",
overview: "Gateway-Status, Einstiegspunkte und eine schnelle Zustandsprüfung.",
channels: "Kanäle und Einstellungen verwalten.",
instances: "Präsenzsignale von verbundenen Clients und Geräten.",
sessions: "Aktive Sitzungen inspizieren und Standardeinstellungen pro Sitzung anpassen.",
usage: "API-Nutzung und Kosten überwachen.",
cron: "Aufweckzeiten und wiederkehrende Agent-Läufe planen.",
skills: "Skill-Verfügbarkeit und API-Schlüsselinjektion verwalten.",
nodes: "Gekoppelte Geräte, Fähigkeiten und Befehlsfreigabe.",
chat: "Direkte Gateway-Chat-Sitzung für schnelle Eingriffe.",
config: "~/.openclaw/openclaw.json sicher bearbeiten.",
debug: "Gateway-Snapshots, Ereignisse und manuelle RPC-Aufrufe.",
logs: "Live-Verfolgung der Gateway-Protokolldateien.",
},
overview: {
access: {
title: "Gateway-Zugang",
subtitle: "Wo sich das Dashboard verbindet und wie es sich authentifiziert.",
wsUrl: "WebSocket-URL",
token: "Gateway-Token",
password: "Passwort (nicht gespeichert)",
sessionKey: "Standard-Sitzungsschlüssel",
language: "Sprache",
connectHint: "Klicken Sie auf Verbinden, um Verbindungsänderungen anzuwenden.",
trustedProxy: "Authentifiziert über vertrauenswürdigen Proxy.",
},
snapshot: {
title: "Snapshot",
subtitle: "Neueste Gateway-Handshake-Informationen.",
status: "Status",
uptime: "Betriebszeit",
tickInterval: "Tick-Intervall",
lastChannelsRefresh: "Letzte Kanalaktualisierung",
channelsHint:
"Verwenden Sie Kanäle, um WhatsApp, Telegram, Discord, Signal oder iMessage zu verknüpfen.",
},
stats: {
instances: "Instanzen",
instancesHint: "Präsenzsignale in den letzten 5 Minuten.",
sessions: "Sitzungen",
sessionsHint: "Letzte vom Gateway verfolgte Sitzungsschlüssel.",
cron: "Cron",
cronNext: "Nächste Ausführung {time}",
},
notes: {
title: "Notizen",
subtitle: "Kurze Hinweise für Remote-Steuerung.",
tailscaleTitle: "Tailscale Serve",
tailscaleText:
"Bevorzugen Sie den Serve-Modus, um das Gateway auf Loopback mit Tailnet-Auth zu halten.",
sessionTitle: "Sitzungshygiene",
sessionText: "Verwenden Sie /new oder sessions.patch, um den Kontext zurückzusetzen.",
cronTitle: "Cron-Erinnerungen",
cronText: "Verwenden Sie isolierte Sitzungen für wiederkehrende Läufe.",
},
auth: {
required:
"Dieses Gateway erfordert Authentifizierung. Fügen Sie ein Token oder Passwort hinzu und klicken Sie auf Verbinden.",
failed:
"Authentifizierung fehlgeschlagen. Kopieren Sie erneut eine URL mit Token über {command}, oder aktualisieren Sie das Token und klicken Sie auf Verbinden.",
},
pairing: {
hint: "Dieses Gerät benötigt eine Pairing-Freigabe vom Gateway-Host.",
mobileHint:
"Auf dem Mobilgerät? Kopieren Sie die vollständige URL (einschließlich #token=...) von openclaw dashboard --no-open auf Ihrem Desktop.",
},
insecure: {
hint: "Diese Seite ist HTTP, daher blockiert der Browser die Geräteidentifikation. Verwenden Sie HTTPS (Tailscale Serve) oder öffnen Sie {url} auf dem Gateway-Host.",
stayHttp: "Wenn Sie bei HTTP bleiben müssen, setzen Sie {config} (nur Token).",
},
},
chat: {
disconnected: "Verbindung zum Gateway getrennt.",
refreshTitle: "Chat-Daten aktualisieren",
thinkingToggle: "Ausgabe des Assistenten ein-/ausblenden",
focusToggle: "Fokusmodus ein-/ausschalten (Seitenleiste + Kopfzeile ausblenden)",
onboardingDisabled: "Während der Einrichtung deaktiviert",
},
languages: {
en: "English",
zhCN: "简体中文 (Vereinfachtes Chinesisch)",
zhTW: "繁體中文 (Traditionelles Chinesisch)",
ptBR: "Português (Brasilianisches Portugiesisch)",
de: "Deutsch",
},
};

View File

@@ -118,5 +118,6 @@ export const en: TranslationMap = {
zhCN: "简体中文 (Simplified Chinese)",
zhTW: "繁體中文 (Traditional Chinese)",
ptBR: "Português (Brazilian Portuguese)",
de: "Deutsch (German)",
},
};

View File

@@ -120,5 +120,6 @@ export const pt_BR: TranslationMap = {
zhCN: "简体中文 (Chinês Simplificado)",
zhTW: "繁體中文 (Chinês Tradicional)",
ptBR: "Português (Português Brasileiro)",
de: "Deutsch (Alemão)",
},
};

View File

@@ -117,5 +117,6 @@ export const zh_CN: TranslationMap = {
zhCN: "简体中文 (简体中文)",
zhTW: "繁體中文 (繁体中文)",
ptBR: "Português (巴西葡萄牙语)",
de: "Deutsch (德语)",
},
};

View File

@@ -117,5 +117,6 @@ export const zh_TW: TranslationMap = {
zhCN: "简体中文 (簡體中文)",
zhTW: "繁體中文 (繁體中文)",
ptBR: "Português (巴西葡萄牙語)",
de: "Deutsch (德語)",
},
};

View File

@@ -1,6 +1,6 @@
import { html } from "lit";
import { ConnectErrorDetailCodes } from "../../../../src/gateway/protocol/connect-error-details.js";
import { t, i18n, type Locale } from "../../i18n/index.ts";
import { t, i18n, SUPPORTED_LOCALES, type Locale } from "../../i18n/index.ts";
import { buildExternalLinkRel, EXTERNAL_LINK_TARGET } from "../external-link.ts";
import { formatRelativeTimestamp, formatDurationHuman } from "../format.ts";
import type { GatewayHelloOk } from "../gateway.ts";
@@ -259,10 +259,10 @@ export function renderOverview(props: OverviewProps) {
props.onSettingsChange({ ...props.settings, locale: v });
}}
>
<option value="en">${t("languages.en")}</option>
<option value="zh-CN">${t("languages.zhCN")}</option>
<option value="zh-TW">${t("languages.zhTW")}</option>
<option value="pt-BR">${t("languages.ptBR")}</option>
${SUPPORTED_LOCALES.map((loc) => {
const key = loc.replace(/-([a-zA-Z])/g, (_, c) => c.toUpperCase());
return html`<option value=${loc}>${t(`languages.${key}`)}</option>`;
})}
</select>
</label>
</div>