Stufe 4c (#764): Kommentare in C#, WebUI-Skripten, CSS und HTML auf Englisch

Rund 2.600 Kommentarblöcke in 310 C#-Dateien übersetzt, Begründungen vollständig
erhalten: blockweise gleiche Anzahl wie vorher, kein Block unter 60 % seiner
Länge, der Code außerhalb der Kommentare per Tokenizer byte-identisch geprüft.
Zitate deutscher Oberflächentexte und Testdaten bleiben deutsch.

WebUI: Kommentare in JS/CSS/HTML übersetzt, JSDoc-@param an die in 4b
umbenannten Parameter angeglichen.

Dabei behoben:
- 4b hatte "tag" (HTML-Tag) als deutsches "Tag" gelesen und zu "dayValue"
  gemacht (makeElement, svg, Tagesübersicht) - zurück auf tag/day.
- Aufgaben "InProgress" bekamen die Plakettenart "laufend", für die es nie eine
  CSS-Klasse gab - jetzt "success" wie laufende Instanzen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-10-04 12:45:03 +02:00
co-authored by Claude Opus 5.5
parent da4ceac7d5
commit 837a634a22
311 changed files with 7238 additions and 7252 deletions
+31 -32
View File
@@ -15,32 +15,32 @@ public sealed class ChatMessage
public string? ToolCallId { get; set; }
/// <summary>
/// Setzt einen Prompt-Caching-Breakpoint auf diese Nachricht.
/// Sets a prompt caching breakpoint on this message.
///
/// Alles, was im Prompt VOR dem Breakpoint steht (Tool-Definitionen, System-Prompt,
/// vorherige Nachrichten), wird beim nächsten Aufruf aus dem Cache gelesen und
/// kostet nur einen Bruchteil. Der Inhalt wird dann als Block-Array statt als
/// einfacher String serialisiert.
/// Everything in the prompt BEFORE the breakpoint (tool definitions, system prompt,
/// previous messages) is read from the cache on the next call and
/// costs only a fraction. The content is then serialized as a block array instead of a
/// plain string.
///
/// Wichtig: Der Prompt-Abschnitt vor dem Breakpoint muss zwischen zwei Aufrufen
/// zeichengenau identisch sein, sonst greift der Cache nicht.
/// Important: the prompt section before the breakpoint must be identical to the character
/// between two calls, otherwise the cache does not apply.
/// </summary>
[JsonIgnore]
public bool CacheBreakpoint { get; set; }
/// <summary>
/// Der Denkverlauf als Klartext, so wie der Anbieter ihn geliefert hat.
/// The reasoning as plain text, as the provider delivered it.
/// </summary>
public string? Reasoning { get; set; }
/// <summary>
/// Der strukturierte Denkverlauf, unverändert.
/// The structured reasoning, unchanged.
///
/// Wird bei Werkzeugnutzung in der folgenden Anfrage zurückgeschickt: Das Modell
/// setzt seine begonnene Antwort fort, statt eine neue zu beginnen. Die Abfolge
/// muss dabei genau der gelieferten entsprechen — umstellen oder verändern macht
/// sie unbrauchbar, und bei signierten Formaten kann der Anbieter sie dann nicht
/// mehr zuordnen. Deshalb wird hier rohes JSON gehalten und nichts ausgelegt.
/// With tool use it is sent back in the following request: the model
/// continues its started answer instead of beginning a new one. The sequence
/// must correspond exactly to the delivered one — reordering or changing it makes
/// it unusable, and with signed formats the provider can then no longer
/// match it. That's why raw JSON is held here and nothing is interpreted.
/// </summary>
public JsonElement? ReasoningDetails { get; set; }
@@ -49,12 +49,12 @@ public sealed class ChatMessage
public static ChatMessage Assistant(string content) => new() { Role = "assistant", Content = content };
/// <summary>
/// Baut die assistant-Nachricht für einen Zug mit Werkzeugaufrufen.
/// Builds the assistant message for a turn with tool calls.
///
/// <paramref name="content"/> ist der Text, den das Modell neben die Aufrufe
/// geschrieben hat („Ich sehe zuerst in der Konfiguration nach"). Er gehört in den
/// Verlauf: Ohne ihn verliert das Modell im nächsten Schritt seinen eigenen Plan,
/// und im Audit steht, <i>dass</i> ein Werkzeug lief, nicht <i>warum</i>.
/// <paramref name="content"/> is the text the model wrote next to the calls
/// ("I'll look in the configuration first"). It belongs in the
/// history: without it the model loses its own plan in the next step,
/// and the audit says <i>that</i> a tool ran, not <i>why</i>.
/// </summary>
public static ChatMessage AssistantWithToolCalls(
List<ToolCall> toolCalls,
@@ -78,13 +78,12 @@ public sealed class ChatMessage
}
/// <summary>
/// Serialisiert <see cref="ChatMessage"/>. Der Inhalt geht normalerweise als einfacher
/// String raus; ist ein <see cref="ChatMessage.CacheBreakpoint"/> gesetzt, stattdessen
/// als Block-Array mit cache_control — das Format, das Anbieter für Prompt-Caching
/// erwarten.
/// Serializes <see cref="ChatMessage"/>. The content normally goes out as a plain
/// string; if a <see cref="ChatMessage.CacheBreakpoint"/> is set, instead
/// as a block array with cache_control — the format providers expect for prompt caching.
///
/// Beim Lesen werden beide Formate akzeptiert: Antworten liefern den Inhalt als String,
/// gespeicherte Kontexte können ihn als Array enthalten.
/// When reading, both formats are accepted: responses deliver the content as a string,
/// stored contexts may contain it as an array.
/// </summary>
public sealed class ChatMessageConverter : JsonConverter<ChatMessage>
{
@@ -126,8 +125,8 @@ public sealed class ChatMessageConverter : JsonConverter<ChatMessage>
message.ToolCallId = reader.GetString();
break;
// Der Denkverlauf wird durchgereicht, nicht ausgelegt. "reasoning_content"
// ist derselbe Inhalt unter anderem Namen; wer zuerst kommt, gewinnt.
// The reasoning is passed through, not interpreted. "reasoning_content"
// is the same content under a different name; whichever comes first wins.
case "reasoning":
case "reasoning_content":
message.Reasoning ??= reader.TokenType == JsonTokenType.String
@@ -138,7 +137,7 @@ public sealed class ChatMessageConverter : JsonConverter<ChatMessage>
break;
case "reasoning_details":
// Clone(), weil der Reader nach dieser Schleife nicht mehr gilt.
// Clone(), because the reader is no longer valid after this loop.
message.ReasoningDetails = reader.TokenType == JsonTokenType.Null
? null
: JsonDocument.ParseValue(ref reader).RootElement.Clone();
@@ -153,7 +152,7 @@ public sealed class ChatMessageConverter : JsonConverter<ChatMessage>
throw new JsonException("ChatMessage: unerwartetes Ende.");
}
/// <summary>Nimmt den Inhalt als String oder als Block-Array entgegen.</summary>
/// <summary>Accepts the content as a string or as a block array.</summary>
private static string? ReadContent(ref Utf8JsonReader reader)
{
if (reader.TokenType == JsonTokenType.Null)
@@ -168,7 +167,7 @@ public sealed class ChatMessageConverter : JsonConverter<ChatMessage>
return null;
}
// Block-Array: die text-Anteile zusammenführen.
// Block array: join the text parts.
var parts = new List<string>();
while (reader.Read() && reader.TokenType != JsonTokenType.EndArray)
{
@@ -224,8 +223,8 @@ public sealed class ChatMessageConverter : JsonConverter<ChatMessage>
if (value.ToolCallId is not null)
writer.WriteString("tool_call_id", value.ToolCallId);
// Unverändert zurück. Das strukturierte Feld hat Vorrang, weil nur es Signatur
// und Reihenfolge trägt; der Klartext geht mit, wenn es keines gibt.
// Back unchanged. The structured field takes precedence because only it carries signature
// and order; the plain text goes along if there is none.
if (value.ReasoningDetails is { } details)
{
writer.WritePropertyName("reasoning_details");