Przeglądaj źródła

feat(app): german translations

Adam 1 miesiąc temu
rodzic
commit
118b4f65da

+ 8 - 2
packages/app/src/context/language.tsx

@@ -6,16 +6,18 @@ import { Persist, persisted } from "@/utils/persist"
 import { dict as en } from "@/i18n/en"
 import { dict as en } from "@/i18n/en"
 import { dict as zh } from "@/i18n/zh"
 import { dict as zh } from "@/i18n/zh"
 import { dict as ko } from "@/i18n/ko"
 import { dict as ko } from "@/i18n/ko"
+import { dict as de } from "@/i18n/de"
 import { dict as uiEn } from "@opencode-ai/ui/i18n/en"
 import { dict as uiEn } from "@opencode-ai/ui/i18n/en"
 import { dict as uiZh } from "@opencode-ai/ui/i18n/zh"
 import { dict as uiZh } from "@opencode-ai/ui/i18n/zh"
 import { dict as uiKo } from "@opencode-ai/ui/i18n/ko"
 import { dict as uiKo } from "@opencode-ai/ui/i18n/ko"
+import { dict as uiDe } from "@opencode-ai/ui/i18n/de"
 
 
-export type Locale = "en" | "zh" | "ko"
+export type Locale = "en" | "zh" | "ko" | "de"
 
 
 type RawDictionary = typeof en & typeof uiEn
 type RawDictionary = typeof en & typeof uiEn
 type Dictionary = i18n.Flatten<RawDictionary>
 type Dictionary = i18n.Flatten<RawDictionary>
 
 
-const LOCALES: readonly Locale[] = ["en", "zh", "ko"]
+const LOCALES: readonly Locale[] = ["en", "zh", "ko", "de"]
 
 
 function detectLocale(): Locale {
 function detectLocale(): Locale {
   if (typeof navigator !== "object") return "en"
   if (typeof navigator !== "object") return "en"
@@ -25,6 +27,7 @@ function detectLocale(): Locale {
     if (!language) continue
     if (!language) continue
     if (language.toLowerCase().startsWith("zh")) return "zh"
     if (language.toLowerCase().startsWith("zh")) return "zh"
     if (language.toLowerCase().startsWith("ko")) return "ko"
     if (language.toLowerCase().startsWith("ko")) return "ko"
+    if (language.toLowerCase().startsWith("de")) return "de"
   }
   }
 
 
   return "en"
   return "en"
@@ -43,6 +46,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
     const locale = createMemo<Locale>(() => {
     const locale = createMemo<Locale>(() => {
       if (store.locale === "zh") return "zh"
       if (store.locale === "zh") return "zh"
       if (store.locale === "ko") return "ko"
       if (store.locale === "ko") return "ko"
+      if (store.locale === "de") return "de"
       return "en"
       return "en"
     })
     })
 
 
@@ -56,6 +60,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
     const dict = createMemo<Dictionary>(() => {
     const dict = createMemo<Dictionary>(() => {
       if (locale() === "en") return base
       if (locale() === "en") return base
       if (locale() === "zh") return { ...base, ...i18n.flatten({ ...zh, ...uiZh }) }
       if (locale() === "zh") return { ...base, ...i18n.flatten({ ...zh, ...uiZh }) }
+      if (locale() === "de") return { ...base, ...i18n.flatten({ ...de, ...uiDe }) }
       return { ...base, ...i18n.flatten({ ...ko, ...uiKo }) }
       return { ...base, ...i18n.flatten({ ...ko, ...uiKo }) }
     })
     })
 
 
@@ -65,6 +70,7 @@ export const { use: useLanguage, provider: LanguageProvider } = createSimpleCont
       en: "language.en",
       en: "language.en",
       zh: "language.zh",
       zh: "language.zh",
       ko: "language.ko",
       ko: "language.ko",
+      de: "language.de",
     }
     }
 
 
     const label = (value: Locale) => t(labelKey[value])
     const label = (value: Locale) => t(labelKey[value])

+ 559 - 0
packages/app/src/i18n/de.ts

@@ -0,0 +1,559 @@
+import { dict as en } from "./en"
+
+type Keys = keyof typeof en
+
+export const dict = {
+  "command.category.suggested": "Vorgeschlagen",
+  "command.category.view": "Ansicht",
+  "command.category.project": "Projekt",
+  "command.category.provider": "Anbieter",
+  "command.category.server": "Server",
+  "command.category.session": "Sitzung",
+  "command.category.theme": "Thema",
+  "command.category.language": "Sprache",
+  "command.category.file": "Datei",
+  "command.category.terminal": "Terminal",
+  "command.category.model": "Modell",
+  "command.category.mcp": "MCP",
+  "command.category.agent": "Agent",
+  "command.category.permissions": "Berechtigungen",
+
+  "theme.scheme.system": "System",
+  "theme.scheme.light": "Hell",
+  "theme.scheme.dark": "Dunkel",
+
+  "command.sidebar.toggle": "Seitenleiste umschalten",
+  "command.project.open": "Projekt öffnen",
+  "command.provider.connect": "Anbieter verbinden",
+  "command.server.switch": "Server wechseln",
+  "command.session.previous": "Vorherige Sitzung",
+  "command.session.next": "Nächste Sitzung",
+  "command.session.archive": "Sitzung archivieren",
+
+  "command.palette": "Befehlspalette",
+
+  "command.theme.cycle": "Thema wechseln",
+  "command.theme.set": "Thema verwenden: {{theme}}",
+  "command.theme.scheme.cycle": "Farbschema wechseln",
+  "command.theme.scheme.set": "Farbschema verwenden: {{scheme}}",
+
+  "command.language.cycle": "Sprache wechseln",
+  "command.language.set": "Sprache verwenden: {{language}}",
+
+  "command.session.new": "Neue Sitzung",
+  "command.file.open": "Datei öffnen",
+  "command.file.open.description": "Dateien und Befehle durchsuchen",
+  "command.terminal.toggle": "Terminal umschalten",
+  "command.review.toggle": "Überprüfung umschalten",
+  "command.terminal.new": "Neues Terminal",
+  "command.terminal.new.description": "Neuen Terminal-Tab erstellen",
+  "command.steps.toggle": "Schritte umschalten",
+  "command.steps.toggle.description": "Schritte für die aktuelle Nachricht anzeigen oder ausblenden",
+  "command.message.previous": "Vorherige Nachricht",
+  "command.message.previous.description": "Zur vorherigen Benutzernachricht gehen",
+  "command.message.next": "Nächste Nachricht",
+  "command.message.next.description": "Zur nächsten Benutzernachricht gehen",
+  "command.model.choose": "Modell wählen",
+  "command.model.choose.description": "Ein anderes Modell auswählen",
+  "command.mcp.toggle": "MCPs umschalten",
+  "command.mcp.toggle.description": "MCPs umschalten",
+  "command.agent.cycle": "Agent wechseln",
+  "command.agent.cycle.description": "Zum nächsten Agenten wechseln",
+  "command.agent.cycle.reverse": "Agent rückwärts wechseln",
+  "command.agent.cycle.reverse.description": "Zum vorherigen Agenten wechseln",
+  "command.model.variant.cycle": "Denkaufwand wechseln",
+  "command.model.variant.cycle.description": "Zum nächsten Aufwandslevel wechseln",
+  "command.permissions.autoaccept.enable": "Änderungen automatisch akzeptieren",
+  "command.permissions.autoaccept.disable": "Automatische Annahme von Änderungen stoppen",
+  "command.session.undo": "Rückgängig",
+  "command.session.undo.description": "Letzte Nachricht rückgängig machen",
+  "command.session.redo": "Wiederherstellen",
+  "command.session.redo.description": "Letzte rückgängig gemachte Nachricht wiederherstellen",
+  "command.session.compact": "Sitzung komprimieren",
+  "command.session.compact.description": "Sitzung zusammenfassen, um die Kontextgröße zu reduzieren",
+  "command.session.fork": "Von Nachricht abzweigen",
+  "command.session.fork.description": "Neue Sitzung aus einer früheren Nachricht erstellen",
+  "command.session.share": "Sitzung teilen",
+  "command.session.share.description": "Diese Sitzung teilen und URL in die Zwischenablage kopieren",
+  "command.session.unshare": "Teilen der Sitzung aufheben",
+  "command.session.unshare.description": "Teilen dieser Sitzung beenden",
+
+  "palette.search.placeholder": "Dateien und Befehle durchsuchen",
+  "palette.empty": "Keine Ergebnisse gefunden",
+  "palette.group.commands": "Befehle",
+  "palette.group.files": "Dateien",
+
+  "dialog.provider.search.placeholder": "Anbieter durchsuchen",
+  "dialog.provider.empty": "Keine Anbieter gefunden",
+  "dialog.provider.group.popular": "Beliebt",
+  "dialog.provider.group.other": "Andere",
+  "dialog.provider.tag.recommended": "Empfohlen",
+  "dialog.provider.anthropic.note": "Mit Claude Pro/Max oder API-Schlüssel verbinden",
+
+  "dialog.model.select.title": "Modell auswählen",
+  "dialog.model.search.placeholder": "Modelle durchsuchen",
+  "dialog.model.empty": "Keine Modellergebnisse",
+  "dialog.model.manage": "Modelle verwalten",
+  "dialog.model.manage.description": "Anpassen, welche Modelle in der Modellauswahl erscheinen.",
+
+  "dialog.model.unpaid.freeModels.title": "Kostenlose Modelle von OpenCode",
+  "dialog.model.unpaid.addMore.title": "Weitere Modelle von beliebten Anbietern hinzufügen",
+
+  "dialog.provider.viewAll": "Alle Anbieter anzeigen",
+
+  "provider.connect.title": "{{provider}} verbinden",
+  "provider.connect.title.anthropicProMax": "Mit Claude Pro/Max anmelden",
+  "provider.connect.selectMethod": "Anmeldemethode für {{provider}} auswählen.",
+  "provider.connect.method.apiKey": "API-Schlüssel",
+  "provider.connect.status.inProgress": "Autorisierung läuft...",
+  "provider.connect.status.waiting": "Warten auf Autorisierung...",
+  "provider.connect.status.failed": "Autorisierung fehlgeschlagen: {{error}}",
+  "provider.connect.apiKey.description":
+    "Geben Sie Ihren {{provider}} API-Schlüssel ein, um Ihr Konto zu verbinden und {{provider}} Modelle in OpenCode zu nutzen.",
+  "provider.connect.apiKey.label": "{{provider}} API-Schlüssel",
+  "provider.connect.apiKey.placeholder": "API-Schlüssel",
+  "provider.connect.apiKey.required": "API-Schlüssel ist erforderlich",
+  "provider.connect.opencodeZen.line1":
+    "OpenCode Zen bietet Ihnen Zugriff auf eine kuratierte Auswahl zuverlässiger, optimierter Modelle für Coding-Agenten.",
+  "provider.connect.opencodeZen.line2":
+    "Mit einem einzigen API-Schlüssel erhalten Sie Zugriff auf Modelle wie Claude, GPT, Gemini, GLM und mehr.",
+  "provider.connect.opencodeZen.visit.prefix": "Besuchen Sie ",
+  "provider.connect.opencodeZen.visit.suffix": ", um Ihren API-Schlüssel zu erhalten.",
+  "provider.connect.oauth.code.visit.prefix": "Besuchen Sie ",
+  "provider.connect.oauth.code.visit.link": "diesen Link",
+  "provider.connect.oauth.code.visit.suffix":
+    ", um Ihren Autorisierungscode zu erhalten, Ihr Konto zu verbinden und {{provider}} Modelle in OpenCode zu nutzen.",
+  "provider.connect.oauth.code.label": "{{method}} Autorisierungscode",
+  "provider.connect.oauth.code.placeholder": "Autorisierungscode",
+  "provider.connect.oauth.code.required": "Autorisierungscode ist erforderlich",
+  "provider.connect.oauth.code.invalid": "Ungültiger Autorisierungscode",
+  "provider.connect.oauth.auto.visit.prefix": "Besuchen Sie ",
+  "provider.connect.oauth.auto.visit.link": "diesen Link",
+  "provider.connect.oauth.auto.visit.suffix":
+    " und geben Sie den untenstehenden Code ein, um Ihr Konto zu verbinden und {{provider}} Modelle in OpenCode zu nutzen.",
+  "provider.connect.oauth.auto.confirmationCode": "Bestätigungscode",
+  "provider.connect.toast.connected.title": "{{provider}} verbunden",
+  "provider.connect.toast.connected.description": "{{provider}} Modelle sind jetzt verfügbar.",
+
+  "model.tag.free": "Kostenlos",
+  "model.tag.latest": "Neueste",
+
+  "common.search.placeholder": "Suchen",
+  "common.loading": "Laden",
+  "common.cancel": "Abbrechen",
+  "common.submit": "Absenden",
+  "common.save": "Speichern",
+  "common.saving": "Speichert...",
+  "common.default": "Standard",
+  "common.attachment": "Anhang",
+
+  "prompt.placeholder.shell": "Shell-Befehl eingeben...",
+  "prompt.placeholder.normal": 'Fragen Sie alles... "{{example}}"',
+  "prompt.mode.shell": "Shell",
+  "prompt.mode.shell.exit": "esc zum Verlassen",
+
+  "prompt.example.1": "Ein TODO in der Codebasis beheben",
+  "prompt.example.2": "Was ist der Tech-Stack dieses Projekts?",
+  "prompt.example.3": "Fehlerhafte Tests beheben",
+  "prompt.example.4": "Erkläre, wie die Authentifizierung funktioniert",
+  "prompt.example.5": "Sicherheitslücken finden und beheben",
+  "prompt.example.6": "Unit-Tests für den Benutzerdienst hinzufügen",
+  "prompt.example.7": "Diese Funktion lesbarer gestalten",
+  "prompt.example.8": "Was bedeutet dieser Fehler?",
+  "prompt.example.9": "Hilf mir, dieses Problem zu debuggen",
+  "prompt.example.10": "API-Dokumentation generieren",
+  "prompt.example.11": "Datenbankabfragen optimieren",
+  "prompt.example.12": "Eingabevalidierung hinzufügen",
+  "prompt.example.13": "Neue Komponente erstellen für...",
+  "prompt.example.14": "Wie deploye ich dieses Projekt?",
+  "prompt.example.15": "Meinen Code auf Best Practices überprüfen",
+  "prompt.example.16": "Fehlerbehandlung zu dieser Funktion hinzufügen",
+  "prompt.example.17": "Erkläre dieses Regex-Muster",
+  "prompt.example.18": "Dies in TypeScript konvertieren",
+  "prompt.example.19": "Logging in der gesamten Codebasis hinzufügen",
+  "prompt.example.20": "Welche Abhängigkeiten sind veraltet?",
+  "prompt.example.21": "Hilf mir, ein Migrationsskript zu schreiben",
+  "prompt.example.22": "Caching für diesen Endpunkt implementieren",
+  "prompt.example.23": "Paginierung zu dieser Liste hinzufügen",
+  "prompt.example.24": "CLI-Befehl erstellen für...",
+  "prompt.example.25": "Wie funktionieren Umgebungsvariablen hier?",
+
+  "prompt.popover.emptyResults": "Keine passenden Ergebnisse",
+  "prompt.popover.emptyCommands": "Keine passenden Befehle",
+  "prompt.dropzone.label": "Bilder oder PDFs hier ablegen",
+  "prompt.slash.badge.custom": "benutzerdefiniert",
+  "prompt.context.active": "aktiv",
+  "prompt.context.includeActiveFile": "Aktive Datei einbeziehen",
+  "prompt.action.attachFile": "Datei anhängen",
+  "prompt.action.send": "Senden",
+  "prompt.action.stop": "Stopp",
+
+  "prompt.toast.pasteUnsupported.title": "Nicht unterstütztes Einfügen",
+  "prompt.toast.pasteUnsupported.description": "Hier können nur Bilder oder PDFs eingefügt werden.",
+  "prompt.toast.modelAgentRequired.title": "Wählen Sie einen Agenten und ein Modell",
+  "prompt.toast.modelAgentRequired.description":
+    "Wählen Sie einen Agenten und ein Modell, bevor Sie eine Eingabe senden.",
+  "prompt.toast.worktreeCreateFailed.title": "Worktree konnte nicht erstellt werden",
+  "prompt.toast.sessionCreateFailed.title": "Sitzung konnte nicht erstellt werden",
+  "prompt.toast.shellSendFailed.title": "Shell-Befehl konnte nicht gesendet werden",
+  "prompt.toast.commandSendFailed.title": "Befehl konnte nicht gesendet werden",
+  "prompt.toast.promptSendFailed.title": "Eingabe konnte nicht gesendet werden",
+
+  "dialog.mcp.title": "MCPs",
+  "dialog.mcp.description": "{{enabled}} von {{total}} aktiviert",
+  "dialog.mcp.empty": "Keine MCPs konfiguriert",
+
+  "mcp.status.connected": "verbunden",
+  "mcp.status.failed": "fehlgeschlagen",
+  "mcp.status.needs_auth": "benötigt Authentifizierung",
+  "mcp.status.disabled": "deaktiviert",
+
+  "dialog.fork.empty": "Keine Nachrichten zum Abzweigen vorhanden",
+
+  "dialog.directory.search.placeholder": "Ordner durchsuchen",
+  "dialog.directory.empty": "Keine Ordner gefunden",
+
+  "dialog.server.title": "Server",
+  "dialog.server.description": "Wechseln Sie den OpenCode-Server, mit dem sich diese App verbindet.",
+  "dialog.server.search.placeholder": "Server durchsuchen",
+  "dialog.server.empty": "Noch keine Server",
+  "dialog.server.add.title": "Server hinzufügen",
+  "dialog.server.add.url": "Server-URL",
+  "dialog.server.add.placeholder": "http://localhost:4096",
+  "dialog.server.add.error": "Verbindung zum Server fehlgeschlagen",
+  "dialog.server.add.checking": "Prüfen...",
+  "dialog.server.add.button": "Hinzufügen",
+  "dialog.server.default.title": "Standardserver",
+  "dialog.server.default.description":
+    "Beim App-Start mit diesem Server verbinden, anstatt einen lokalen Server zu starten. Erfordert Neustart.",
+  "dialog.server.default.none": "Kein Server ausgewählt",
+  "dialog.server.default.set": "Aktuellen Server als Standard setzen",
+  "dialog.server.default.clear": "Löschen",
+
+  "dialog.project.edit.title": "Projekt bearbeiten",
+  "dialog.project.edit.name": "Name",
+  "dialog.project.edit.icon": "Icon",
+  "dialog.project.edit.icon.alt": "Projekt-Icon",
+  "dialog.project.edit.icon.hint": "Klicken oder Bild ziehen",
+  "dialog.project.edit.icon.recommended": "Empfohlen: 128x128px",
+  "dialog.project.edit.color": "Farbe",
+
+  "context.breakdown.title": "Kontext-Aufschlüsselung",
+  "context.breakdown.note":
+    'Ungefähre Aufschlüsselung der Eingabe-Token. "Andere" beinhaltet Werkzeugdefinitionen und Overhead.',
+  "context.breakdown.system": "System",
+  "context.breakdown.user": "Benutzer",
+  "context.breakdown.assistant": "Assistent",
+  "context.breakdown.tool": "Werkzeugaufrufe",
+  "context.breakdown.other": "Andere",
+
+  "context.systemPrompt.title": "System-Prompt",
+  "context.rawMessages.title": "Rohdaten der Nachrichten",
+
+  "context.stats.session": "Sitzung",
+  "context.stats.messages": "Nachrichten",
+  "context.stats.provider": "Anbieter",
+  "context.stats.model": "Modell",
+  "context.stats.limit": "Kontextlimit",
+  "context.stats.totalTokens": "Gesamt-Token",
+  "context.stats.usage": "Nutzung",
+  "context.stats.inputTokens": "Eingabe-Token",
+  "context.stats.outputTokens": "Ausgabe-Token",
+  "context.stats.reasoningTokens": "Reasoning-Token",
+  "context.stats.cacheTokens": "Cache-Token (lesen/schreiben)",
+  "context.stats.userMessages": "Benutzernachrichten",
+  "context.stats.assistantMessages": "Assistentennachrichten",
+  "context.stats.totalCost": "Gesamtkosten",
+  "context.stats.sessionCreated": "Sitzung erstellt",
+  "context.stats.lastActivity": "Letzte Aktivität",
+
+  "context.usage.tokens": "Token",
+  "context.usage.usage": "Nutzung",
+  "context.usage.cost": "Kosten",
+  "context.usage.clickToView": "Klicken, um Kontext anzuzeigen",
+
+  "language.en": "Englisch",
+  "language.zh": "Chinesisch",
+  "language.ko": "Koreanisch",
+  "language.de": "Deutsch",
+
+  "toast.language.title": "Sprache",
+  "toast.language.description": "Zu {{language}} gewechselt",
+
+  "toast.theme.title": "Thema gewechselt",
+  "toast.scheme.title": "Farbschema",
+
+  "toast.permissions.autoaccept.on.title": "Änderungen werden automatisch akzeptiert",
+  "toast.permissions.autoaccept.on.description": "Bearbeitungs- und Schreibrechte werden automatisch genehmigt",
+  "toast.permissions.autoaccept.off.title": "Automatische Annahme von Änderungen gestoppt",
+  "toast.permissions.autoaccept.off.description": "Bearbeitungs- und Schreibrechte erfordern Genehmigung",
+
+  "toast.model.none.title": "Kein Modell ausgewählt",
+  "toast.model.none.description": "Verbinden Sie einen Anbieter, um diese Sitzung zusammenzufassen",
+
+  "toast.file.loadFailed.title": "Datei konnte nicht geladen werden",
+
+  "toast.session.share.copyFailed.title": "URL konnte nicht in die Zwischenablage kopiert werden",
+  "toast.session.share.success.title": "Sitzung geteilt",
+  "toast.session.share.success.description": "Teilen-URL in die Zwischenablage kopiert!",
+  "toast.session.share.failed.title": "Sitzung konnte nicht geteilt werden",
+  "toast.session.share.failed.description": "Beim Teilen der Sitzung ist ein Fehler aufgetreten",
+
+  "toast.session.unshare.success.title": "Teilen der Sitzung aufgehoben",
+  "toast.session.unshare.success.description": "Teilen der Sitzung erfolgreich aufgehoben!",
+  "toast.session.unshare.failed.title": "Aufheben des Teilens fehlgeschlagen",
+  "toast.session.unshare.failed.description": "Beim Aufheben des Teilens ist ein Fehler aufgetreten",
+
+  "toast.session.listFailed.title": "Sitzungen für {{project}} konnten nicht geladen werden",
+
+  "toast.update.title": "Update verfügbar",
+  "toast.update.description": "Eine neue Version von OpenCode ({{version}}) ist zur Installation verfügbar.",
+  "toast.update.action.installRestart": "Installieren und neu starten",
+  "toast.update.action.notYet": "Noch nicht",
+
+  "error.page.title": "Etwas ist schiefgelaufen",
+  "error.page.description": "Beim Laden der Anwendung ist ein Fehler aufgetreten.",
+  "error.page.details.label": "Fehlerdetails",
+  "error.page.action.restart": "Neustart",
+  "error.page.action.checking": "Prüfen...",
+  "error.page.action.checkUpdates": "Nach Updates suchen",
+  "error.page.action.updateTo": "Auf {{version}} aktualisieren",
+  "error.page.report.prefix": "Bitte melden Sie diesen Fehler dem OpenCode-Team",
+  "error.page.report.discord": "auf Discord",
+  "error.page.version": "Version: {{version}}",
+
+  "error.dev.rootNotFound":
+    "Wurzelelement nicht gefunden. Haben Sie vergessen, es in Ihre index.html aufzunehmen? Oder wurde das id-Attribut falsch geschrieben?",
+
+  "error.globalSync.connectFailed": "Verbindung zum Server fehlgeschlagen. Läuft ein Server unter `{{url}}`?",
+
+  "error.chain.unknown": "Unbekannter Fehler",
+  "error.chain.causedBy": "Verursacht durch:",
+  "error.chain.apiError": "API-Fehler",
+  "error.chain.status": "Status: {{status}}",
+  "error.chain.retryable": "Wiederholbar: {{retryable}}",
+  "error.chain.responseBody": "Antwort-Body:\n{{body}}",
+  "error.chain.didYouMean": "Meinten Sie: {{suggestions}}",
+  "error.chain.modelNotFound": "Modell nicht gefunden: {{provider}}/{{model}}",
+  "error.chain.checkConfig": "Überprüfen Sie Ihre Konfiguration (opencode.json) auf Anbieter-/Modellnamen",
+  "error.chain.mcpFailed":
+    'MCP-Server "{{name}}" fehlgeschlagen. Hinweis: OpenCode unterstützt noch keine MCP-Authentifizierung.',
+  "error.chain.providerAuthFailed": "Anbieter-Authentifizierung fehlgeschlagen ({{provider}}): {{message}}",
+  "error.chain.providerInitFailed":
+    'Anbieter "{{provider}}" konnte nicht initialisiert werden. Überprüfen Sie Anmeldeinformationen und Konfiguration.',
+  "error.chain.configJsonInvalid": "Konfigurationsdatei unter {{path}} ist kein gültiges JSON(C)",
+  "error.chain.configJsonInvalidWithMessage":
+    "Konfigurationsdatei unter {{path}} ist kein gültiges JSON(C): {{message}}",
+  "error.chain.configDirectoryTypo":
+    'Verzeichnis "{{dir}}" in {{path}} ist ungültig. Benennen Sie das Verzeichnis in "{{suggestion}}" um oder entfernen Sie es. Dies ist ein häufiger Tippfehler.',
+  "error.chain.configFrontmatterError": "Frontmatter in {{path}} konnte nicht geparst werden:\n{{message}}",
+  "error.chain.configInvalid": "Konfigurationsdatei unter {{path}} ist ungültig",
+  "error.chain.configInvalidWithMessage": "Konfigurationsdatei unter {{path}} ist ungültig: {{message}}",
+
+  "notification.permission.title": "Berechtigung erforderlich",
+  "notification.permission.description": "{{sessionTitle}} in {{projectName}} benötigt Berechtigung",
+  "notification.question.title": "Frage",
+  "notification.question.description": "{{sessionTitle}} in {{projectName}} hat eine Frage",
+  "notification.action.goToSession": "Zur Sitzung gehen",
+
+  "notification.session.responseReady.title": "Antwort bereit",
+  "notification.session.error.title": "Sitzungsfehler",
+  "notification.session.error.fallbackDescription": "Ein Fehler ist aufgetreten",
+
+  "home.recentProjects": "Letzte Projekte",
+  "home.empty.title": "Keine letzten Projekte",
+  "home.empty.description": "Starten Sie, indem Sie ein lokales Projekt öffnen",
+
+  "session.tab.session": "Sitzung",
+  "session.tab.review": "Überprüfung",
+  "session.tab.context": "Kontext",
+  "session.review.filesChanged": "{{count}} Dateien geändert",
+  "session.review.loadingChanges": "Lade Änderungen...",
+  "session.review.empty": "Noch keine Änderungen in dieser Sitzung",
+  "session.messages.renderEarlier": "Frühere Nachrichten rendern",
+  "session.messages.loadingEarlier": "Lade frühere Nachrichten...",
+  "session.messages.loadEarlier": "Frühere Nachrichten laden",
+  "session.messages.loading": "Lade Nachrichten...",
+
+  "session.context.addToContext": "{{selection}} zum Kontext hinzufügen",
+
+  "session.new.worktree.main": "Haupt-Branch",
+  "session.new.worktree.mainWithBranch": "Haupt-Branch ({{branch}})",
+  "session.new.worktree.create": "Neuen Worktree erstellen",
+  "session.new.lastModified": "Zuletzt geändert",
+
+  "session.header.search.placeholder": "{{project}} durchsuchen",
+
+  "session.share.popover.title": "Im Web veröffentlichen",
+  "session.share.popover.description.shared":
+    "Diese Sitzung ist öffentlich im Web. Sie ist für jeden mit dem Link zugänglich.",
+  "session.share.popover.description.unshared":
+    "Sitzung öffentlich im Web teilen. Sie wird für jeden mit dem Link zugänglich sein.",
+  "session.share.action.share": "Teilen",
+  "session.share.action.publish": "Veröffentlichen",
+  "session.share.action.publishing": "Veröffentliche...",
+  "session.share.action.unpublish": "Veröffentlichung aufheben",
+  "session.share.action.unpublishing": "Hebe Veröffentlichung auf...",
+  "session.share.action.view": "Ansehen",
+  "session.share.copy.copied": "Kopiert",
+  "session.share.copy.copyLink": "Link kopieren",
+
+  "lsp.tooltip.none": "Keine LSP-Server",
+  "lsp.label.connected": "{{count}} LSP",
+
+  "prompt.loading": "Lade Prompt...",
+  "terminal.loading": "Lade Terminal...",
+  "terminal.title": "Terminal",
+  "terminal.title.numbered": "Terminal {{number}}",
+
+  "common.closeTab": "Tab schließen",
+  "common.dismiss": "Verwerfen",
+  "common.requestFailed": "Anfrage fehlgeschlagen",
+  "common.moreOptions": "Weitere Optionen",
+  "common.learnMore": "Mehr erfahren",
+  "common.rename": "Umbenennen",
+  "common.reset": "Zurücksetzen",
+  "common.delete": "Löschen",
+  "common.close": "Schließen",
+  "common.edit": "Bearbeiten",
+  "common.loadMore": "Mehr laden",
+
+  "sidebar.settings": "Einstellungen",
+  "sidebar.help": "Hilfe",
+  "sidebar.workspaces.enable": "Arbeitsbereiche aktivieren",
+  "sidebar.workspaces.disable": "Arbeitsbereiche deaktivieren",
+  "sidebar.gettingStarted.title": "Erste Schritte",
+  "sidebar.gettingStarted.line1": "OpenCode enthält kostenlose Modelle, damit Sie sofort loslegen können.",
+  "sidebar.gettingStarted.line2":
+    "Verbinden Sie einen beliebigen Anbieter, um Modelle wie Claude, GPT, Gemini usw. zu nutzen.",
+  "sidebar.project.recentSessions": "Letzte Sitzungen",
+  "sidebar.project.viewAllSessions": "Alle Sitzungen anzeigen",
+
+  "settings.section.desktop": "Desktop",
+  "settings.tab.general": "Allgemein",
+  "settings.tab.shortcuts": "Tastenkombinationen",
+
+  "settings.general.section.appearance": "Erscheinungsbild",
+  "settings.general.section.notifications": "Systembenachrichtigungen",
+  "settings.general.section.sounds": "Soundeffekte",
+
+  "settings.general.row.language.title": "Sprache",
+  "settings.general.row.language.description": "Die Anzeigesprache für OpenCode ändern",
+  "settings.general.row.appearance.title": "Erscheinungsbild",
+  "settings.general.row.appearance.description": "Anpassen, wie OpenCode auf Ihrem Gerät aussieht",
+  "settings.general.row.theme.title": "Thema",
+  "settings.general.row.theme.description": "Das Thema von OpenCode anpassen.",
+  "settings.general.row.font.title": "Schriftart",
+  "settings.general.row.font.description": "Die in Codeblöcken verwendete Monospace-Schriftart anpassen",
+
+  "settings.general.notifications.agent.title": "Agent",
+  "settings.general.notifications.agent.description":
+    "Systembenachrichtigung anzeigen, wenn der Agent fertig ist oder Aufmerksamkeit benötigt",
+  "settings.general.notifications.permissions.title": "Berechtigungen",
+  "settings.general.notifications.permissions.description":
+    "Systembenachrichtigung anzeigen, wenn eine Berechtigung erforderlich ist",
+  "settings.general.notifications.errors.title": "Fehler",
+  "settings.general.notifications.errors.description": "Systembenachrichtigung anzeigen, wenn ein Fehler auftritt",
+
+  "settings.general.sounds.agent.title": "Agent",
+  "settings.general.sounds.agent.description": "Ton abspielen, wenn der Agent fertig ist oder Aufmerksamkeit benötigt",
+  "settings.general.sounds.permissions.title": "Berechtigungen",
+  "settings.general.sounds.permissions.description": "Ton abspielen, wenn eine Berechtigung erforderlich ist",
+  "settings.general.sounds.errors.title": "Fehler",
+  "settings.general.sounds.errors.description": "Ton abspielen, wenn ein Fehler auftritt",
+
+  "settings.shortcuts.title": "Tastenkombinationen",
+  "settings.shortcuts.reset.button": "Auf Standard zurücksetzen",
+  "settings.shortcuts.reset.toast.title": "Tastenkombinationen zurückgesetzt",
+  "settings.shortcuts.reset.toast.description": "Die Tastenkombinationen wurden auf die Standardwerte zurückgesetzt.",
+  "settings.shortcuts.conflict.title": "Tastenkombination bereits in Verwendung",
+  "settings.shortcuts.conflict.description": "{{keybind}} ist bereits {{titles}} zugewiesen.",
+  "settings.shortcuts.unassigned": "Nicht zugewiesen",
+  "settings.shortcuts.pressKeys": "Tasten drücken",
+
+  "settings.shortcuts.group.general": "Allgemein",
+  "settings.shortcuts.group.session": "Sitzung",
+  "settings.shortcuts.group.navigation": "Navigation",
+  "settings.shortcuts.group.modelAndAgent": "Modell und Agent",
+  "settings.shortcuts.group.terminal": "Terminal",
+  "settings.shortcuts.group.prompt": "Prompt",
+
+  "settings.providers.title": "Anbieter",
+  "settings.providers.description": "Anbietereinstellungen können hier konfiguriert werden.",
+  "settings.models.title": "Modelle",
+  "settings.models.description": "Modelleinstellungen können hier konfiguriert werden.",
+  "settings.agents.title": "Agenten",
+  "settings.agents.description": "Agenteneinstellungen können hier konfiguriert werden.",
+  "settings.commands.title": "Befehle",
+  "settings.commands.description": "Befehlseinstellungen können hier konfiguriert werden.",
+  "settings.mcp.title": "MCP",
+  "settings.mcp.description": "MCP-Einstellungen können hier konfiguriert werden.",
+
+  "settings.permissions.title": "Berechtigungen",
+  "settings.permissions.description": "Steuern Sie, welche Tools der Server standardmäßig verwenden darf.",
+  "settings.permissions.section.tools": "Tools",
+  "settings.permissions.toast.updateFailed.title": "Berechtigungen konnten nicht aktualisiert werden",
+
+  "settings.permissions.action.allow": "Erlauben",
+  "settings.permissions.action.ask": "Fragen",
+  "settings.permissions.action.deny": "Verweigern",
+
+  "settings.permissions.tool.read.title": "Lesen",
+  "settings.permissions.tool.read.description": "Lesen einer Datei (stimmt mit dem Dateipfad überein)",
+  "settings.permissions.tool.edit.title": "Bearbeiten",
+  "settings.permissions.tool.edit.description":
+    "Dateien ändern, einschließlich Bearbeitungen, Schreibvorgängen, Patches und Mehrfachbearbeitungen",
+  "settings.permissions.tool.glob.title": "Glob",
+  "settings.permissions.tool.glob.description": "Dateien mithilfe von Glob-Mustern abgleichen",
+  "settings.permissions.tool.grep.title": "Grep",
+  "settings.permissions.tool.grep.description": "Dateiinhalte mit regulären Ausdrücken durchsuchen",
+  "settings.permissions.tool.list.title": "Auflisten",
+  "settings.permissions.tool.list.description": "Dateien in einem Verzeichnis auflisten",
+  "settings.permissions.tool.bash.title": "Bash",
+  "settings.permissions.tool.bash.description": "Shell-Befehle ausführen",
+  "settings.permissions.tool.task.title": "Aufgabe",
+  "settings.permissions.tool.task.description": "Unteragenten starten",
+  "settings.permissions.tool.skill.title": "Fähigkeit",
+  "settings.permissions.tool.skill.description": "Eine Fähigkeit nach Namen laden",
+  "settings.permissions.tool.lsp.title": "LSP",
+  "settings.permissions.tool.lsp.description": "Language-Server-Abfragen ausführen",
+  "settings.permissions.tool.todoread.title": "Todo lesen",
+  "settings.permissions.tool.todoread.description": "Die Todo-Liste lesen",
+  "settings.permissions.tool.todowrite.title": "Todo schreiben",
+  "settings.permissions.tool.todowrite.description": "Die Todo-Liste aktualisieren",
+  "settings.permissions.tool.webfetch.title": "Web-Abruf",
+  "settings.permissions.tool.webfetch.description": "Inhalt von einer URL abrufen",
+  "settings.permissions.tool.websearch.title": "Web-Suche",
+  "settings.permissions.tool.websearch.description": "Das Web durchsuchen",
+  "settings.permissions.tool.codesearch.title": "Code-Suche",
+  "settings.permissions.tool.codesearch.description": "Code im Web durchsuchen",
+  "settings.permissions.tool.external_directory.title": "Externes Verzeichnis",
+  "settings.permissions.tool.external_directory.description": "Zugriff auf Dateien außerhalb des Projektverzeichnisses",
+  "settings.permissions.tool.doom_loop.title": "Doom Loop",
+  "settings.permissions.tool.doom_loop.description": "Wiederholte Tool-Aufrufe mit identischer Eingabe erkennen",
+
+  "workspace.new": "Neuer Arbeitsbereich",
+  "workspace.type.local": "lokal",
+  "workspace.type.sandbox": "Sandbox",
+  "workspace.create.failed.title": "Arbeitsbereich konnte nicht erstellt werden",
+  "workspace.delete.failed.title": "Arbeitsbereich konnte nicht gelöscht werden",
+  "workspace.resetting.title": "Arbeitsbereich wird zurückgesetzt",
+  "workspace.resetting.description": "Dies kann eine Minute dauern.",
+  "workspace.reset.failed.title": "Arbeitsbereich konnte nicht zurückgesetzt werden",
+  "workspace.reset.success.title": "Arbeitsbereich zurückgesetzt",
+  "workspace.reset.success.description": "Der Arbeitsbereich entspricht jetzt dem Standard-Branch.",
+  "workspace.status.checking": "Suche nach nicht zusammengeführten Änderungen...",
+  "workspace.status.error": "Git-Status konnte nicht überprüft werden.",
+  "workspace.status.clean": "Keine nicht zusammengeführten Änderungen erkannt.",
+  "workspace.status.dirty": "Nicht zusammengeführte Änderungen in diesem Arbeitsbereich erkannt.",
+  "workspace.delete.title": "Arbeitsbereich löschen",
+  "workspace.delete.confirm": 'Arbeitsbereich "{{name}}" löschen?',
+  "workspace.delete.button": "Arbeitsbereich löschen",
+  "workspace.reset.title": "Arbeitsbereich zurücksetzen",
+  "workspace.reset.confirm": 'Arbeitsbereich "{{name}}" zurücksetzen?',
+  "workspace.reset.button": "Arbeitsbereich zurücksetzen",
+  "workspace.reset.archived.none": "Keine aktiven Sitzungen werden archiviert.",
+  "workspace.reset.archived.one": "1 Sitzung wird archiviert.",
+  "workspace.reset.archived.many": "{{count}} Sitzungen werden archiviert.",
+  "workspace.reset.note": "Dadurch wird der Arbeitsbereich auf den Standard-Branch zurückgesetzt.",
+} satisfies Partial<Record<Keys, string>>

+ 1 - 0
packages/app/src/i18n/en.ts

@@ -269,6 +269,7 @@ export const dict = {
   "language.en": "English",
   "language.en": "English",
   "language.zh": "Chinese",
   "language.zh": "Chinese",
   "language.ko": "Korean",
   "language.ko": "Korean",
+  "language.de": "German",
 
 
   "toast.language.title": "Language",
   "toast.language.title": "Language",
   "toast.language.description": "Switched to {{language}}",
   "toast.language.description": "Switched to {{language}}",

+ 1 - 0
packages/app/src/i18n/ko.ts

@@ -272,6 +272,7 @@ export const dict = {
   "language.en": "영어",
   "language.en": "영어",
   "language.zh": "중국어",
   "language.zh": "중국어",
   "language.ko": "한국어",
   "language.ko": "한국어",
+  "language.de": "독일어",
 
 
   "toast.language.title": "언어",
   "toast.language.title": "언어",
   "toast.language.description": "{{language}}(으)로 전환됨",
   "toast.language.description": "{{language}}(으)로 전환됨",

+ 1 - 0
packages/app/src/i18n/zh.ts

@@ -268,6 +268,7 @@ export const dict = {
   "language.en": "英语",
   "language.en": "英语",
   "language.zh": "中文",
   "language.zh": "中文",
   "language.ko": "韩语",
   "language.ko": "韩语",
+  "language.de": "德语",
 
 
   "toast.language.title": "语言",
   "toast.language.title": "语言",
   "toast.language.description": "已切换到{{language}}",
   "toast.language.description": "已切换到{{language}}",

+ 94 - 0
packages/ui/src/i18n/de.ts

@@ -0,0 +1,94 @@
+import { dict as en } from "./en"
+
+type Keys = keyof typeof en
+
+export const dict = {
+  "ui.sessionReview.title": "Sitzungsänderungen",
+  "ui.sessionReview.diffStyle.unified": "Vereinheitlicht",
+  "ui.sessionReview.diffStyle.split": "Geteilt",
+  "ui.sessionReview.expandAll": "Alle erweitern",
+  "ui.sessionReview.collapseAll": "Alle reduzieren",
+
+  "ui.sessionTurn.steps.show": "Schritte anzeigen",
+  "ui.sessionTurn.steps.hide": "Schritte ausblenden",
+  "ui.sessionTurn.summary.response": "Antwort",
+  "ui.sessionTurn.diff.showMore": "Weitere Änderungen anzeigen ({{count}})",
+
+  "ui.sessionTurn.retry.retrying": "erneuter Versuch",
+  "ui.sessionTurn.retry.inSeconds": "in {{seconds}}s",
+
+  "ui.sessionTurn.status.delegating": "Arbeit delegieren",
+  "ui.sessionTurn.status.planning": "Nächste Schritte planen",
+  "ui.sessionTurn.status.gatheringContext": "Kontext sammeln",
+  "ui.sessionTurn.status.searchingCodebase": "Codebasis durchsuchen",
+  "ui.sessionTurn.status.searchingWeb": "Web durchsuchen",
+  "ui.sessionTurn.status.makingEdits": "Änderungen vornehmen",
+  "ui.sessionTurn.status.runningCommands": "Befehle ausführen",
+  "ui.sessionTurn.status.thinking": "Denken",
+  "ui.sessionTurn.status.thinkingWithTopic": "Denken - {{topic}}",
+  "ui.sessionTurn.status.gatheringThoughts": "Gedanken sammeln",
+  "ui.sessionTurn.status.consideringNextSteps": "Nächste Schritte erwägen",
+
+  "ui.messagePart.diagnostic.error": "Fehler",
+  "ui.messagePart.title.edit": "Bearbeiten",
+  "ui.messagePart.title.write": "Schreiben",
+  "ui.messagePart.option.typeOwnAnswer": "Eigene Antwort eingeben",
+  "ui.messagePart.review.title": "Antworten überprüfen",
+
+  "ui.list.loading": "Laden",
+  "ui.list.empty": "Keine Ergebnisse",
+  "ui.list.emptyWithFilter.prefix": "Keine Ergebnisse für",
+  "ui.list.emptyWithFilter.suffix": "",
+
+  "ui.messageNav.newMessage": "Neue Nachricht",
+
+  "ui.textField.copyToClipboard": "In die Zwischenablage kopieren",
+  "ui.textField.copied": "Kopiert",
+
+  "ui.imagePreview.alt": "Bildvorschau",
+
+  "ui.tool.read": "Lesen",
+  "ui.tool.list": "Auflisten",
+  "ui.tool.glob": "Glob",
+  "ui.tool.grep": "Grep",
+  "ui.tool.webfetch": "Webabruf",
+  "ui.tool.shell": "Shell",
+  "ui.tool.patch": "Patch",
+  "ui.tool.todos": "Aufgaben",
+  "ui.tool.todos.read": "Aufgaben lesen",
+  "ui.tool.questions": "Fragen",
+  "ui.tool.agent": "{{type}} Agent",
+
+  "ui.common.file.one": "Datei",
+  "ui.common.file.other": "Dateien",
+  "ui.common.question.one": "Frage",
+  "ui.common.question.other": "Fragen",
+
+  "ui.common.add": "Hinzufügen",
+  "ui.common.cancel": "Abbrechen",
+  "ui.common.confirm": "Bestätigen",
+  "ui.common.dismiss": "Verwerfen",
+  "ui.common.next": "Weiter",
+  "ui.common.submit": "Absenden",
+
+  "ui.permission.deny": "Verweigern",
+  "ui.permission.allowAlways": "Immer erlauben",
+  "ui.permission.allowOnce": "Einmal erlauben",
+
+  "ui.message.expand": "Nachricht erweitern",
+  "ui.message.collapse": "Nachricht reduzieren",
+  "ui.message.copy": "Kopieren",
+  "ui.message.copied": "Kopiert!",
+  "ui.message.attachment.alt": "Anhang",
+
+  "ui.patch.action.deleted": "Gelöscht",
+  "ui.patch.action.created": "Erstellt",
+  "ui.patch.action.moved": "Verschoben",
+  "ui.patch.action.patched": "Gepatched",
+
+  "ui.question.subtitle.answered": "{{count}} beantwortet",
+  "ui.question.answer.none": "(keine Antwort)",
+  "ui.question.review.notAnswered": "(nicht beantwortet)",
+  "ui.question.multiHint": "(alle zutreffenden auswählen)",
+  "ui.question.custom.placeholder": "Geben Sie Ihre Antwort ein...",
+} satisfies Partial<Record<Keys, string>>