Deutsch
Übersetzung
Verbinden Sie init({ translation }) mit einem TranslationProvider, um geeignete Felder aus der Standardsprache in eine aktivierte Sprachversion zu übersetzen.
Editor-Einrichtung
ts
import { init } from "@templatical/editor";
import "@templatical/editor/style.css";
import { createHttpTranslationProvider } from "@templatical/ai-kit/client";
const editor = await init({
container: "#editor",
localization: { locales: ["en", "de"], doNotTranslate: ["Sable"] },
translation: createHttpTranslationProvider({ url: "/api" }),
});Für eine statische Seite ohne Bundler binden Sie den Editor wie im Schnellstart über das CDN ein; translation kann jedes Objekt mit einer translate-Methode sein. Fügen Sie unter Sprachen Deutsch hinzu und wählen Sie es aus. Übersetzen erscheint nur mit translation, konfigurierter localization und einer aktivierten Nicht-Standardsprachansicht. Weder AiProvider noch ein geladenes Template sind nötig; ohne Template ist templateId gleich null. Ohne translation fehlt die Übersetzungsoberfläche. Lokalisierte Templates beschreibt die Aktionen und den JSON-Strings-Rundlauf, Betreffzeilen den Text außerhalb des Templates.
Provider-Vertrag
TranslationProvider.translate(request, signal) liefert ein TranslationResult. Die vollständigen Deklarationen einschließlich der reinen Strings-Helfer:
ts
import type {
CustomBlockDefinition,
Operation,
SyntaxPreset,
TemplateContent,
} from "@templatical/types";
export type TranslationErrorCode =
"rate_limited" | "limit_reached" | "content_too_large" | "translation_failed";
export interface TranslationField {
/** A block field id, or `settings/<path>`. */
id: string;
/** Merge and logic tags masked as numbered placeholders. */
text: string;
format: "html" | "text";
}
export interface TranslationRequest {
templateId: string | null;
sourceLocale: string;
targetLocale: string;
/** At most 50 fields and 20,000 characters of text per call. */
fields: TranslationField[];
}
export interface TranslationResult {
fields: { id: string; text: string }[];
}
/** A translation backend. Reject with an SdkError or, when aborted, an AbortError. */
export interface TranslationProvider {
translate(
request: TranslationRequest,
signal: AbortSignal,
): Promise<TranslationResult>;
}
export const TRANSLATION_MAX_FIELDS = 50;
export const TRANSLATION_MAX_CHARS = 20_000;
export interface LocaleString {
id: string;
format: "html" | "text";
/** The masked default value. */
source: string;
/** The hash of the unmasked default at extraction time. */
sourceHash: string;
/** The stored translation masked with the source's token ids. */
text?: string;
}
export declare const TRANSLATABLE_HTML_FIELDS: Readonly<
Record<string, readonly string[]>
>;
export declare function maskTags(
text: string,
syntax: SyntaxPreset,
format: "html" | "text",
options?: { doNotTranslate?: readonly string[] },
): { text: string; tokens: string[] };
export declare function unmaskTags(
text: string,
tokens: string[],
): string | null;
export declare function localeStrings(
content: TemplateContent,
locale: string,
options: {
definitions?: readonly CustomBlockDefinition[];
syntax: SyntaxPreset;
only: "gaps" | "all";
blockId?: string;
doNotTranslate?: readonly string[];
},
): { strings: LocaleString[]; tokens: Record<string, string[]> };
export declare function applyLocaleStrings(
content: TemplateContent,
locale: string,
values: { id: string; text: string; sourceHash: string }[],
options: {
definitions?: readonly CustomBlockDefinition[];
syntax: SyntaxPreset;
tokens: Record<string, string[]>;
doNotTranslate?: readonly string[];
},
): {
operations: Operation[];
rejected: {
id: string;
reason: "tags_changed" | "unknown_field" | "not_translatable";
}[];
};TranslationRequest.fields enthält 1–50 Felder mit zusammen höchstens 20,000 UTF-16-Einheiten (text.length). IDs sind <blockId>/<path> oder settings/<path>. sourceLocale ist die Standardsprache, targetLocale die gewählte Version. templateId ist die ID des geladenen Templates oder null. Das Kit prüft eindeutige, nichtleere IDs und Sprach-Tags. Ergebnisse dürfen unvollständig sein; ausgelassene Felder behalten ihren bisherigen Wert, unbekannte IDs ignoriert der Editor.
Nur content von Titel und Absatz verwendet format: "html"; alle anderen geeigneten Felder verwenden "text". HTML-Blöcke sind vom Übersetzen ausgeschlossen und bleiben von Hand bearbeitbar. Leere Standardwerte, set-Überschreibungen und ausgeblendete Blöcke werden ebenfalls ausgelassen.
Der Editor maskiert ganze Merge-/Logik-Chips in HTML und durch die aktive Syntax erkannte rohe Tags in beiden Formaten. Textinhalt verwendet <x id="n"/>, HTML-Attributwerte __xn__; beide teilen eine ID-Folge ab 1 in Quellreihenfolge pro Feld. Geben Sie jede ID genau einmal in einem der akzeptierten Formate zurück; <x id="n"></x> wird ebenfalls akzeptiert. Platzhalter dürfen ihre Position ändern. Fehlende, doppelte oder zusätzliche IDs erhalten den alten Wert und melden tags_changed. Vorhandene Übersetzungen werden mit den Quell-Token-IDs maskiert. Stimmen ihre Tags nicht mehr überein, wird die alte Übersetzung nicht gesendet und das Feld aus dem aktuellen Standard übersetzt. localeStrings behält die maskierte aktuelle Quelle und sourceHash bei und lässt text weg; allein die Abdeckung bestimmt die Auswahl von Lücken oder allen Feldern. Zurückgegebenes HTML durchläuft den DOMPurify-basierten sanitizeHtml des Editors mit geschützten Tokens; anschließend werden die ursprünglichen Tags bytegetreu wiederhergestellt. Die reinen Helfer bereinigen HTML nicht; Headless-Aufrufer übernehmen diesen Schritt.
localization.doNotTranslate?: string[] schützt konfigurierte Begriffe mit derselben exakten Platzhalterprüfung. Ohne Angabe werden nur Merge- und Logik-Tags geschützt. init() akzeptiert höchstens 500 Begriffe mit jeweils 1–100 Zeichen nach dem Trimmen; ungültige Eingaben werden mit SdkError abgelehnt: localization.doNotTranslate must be an array of at most 500 terms, each 1–100 characters. Der Abgleich unterscheidet Groß- und Kleinschreibung und erfasst ganze Wörter: Unicode-Buchstaben, Zahlen und _ sind Wortzeichen. In html-Feldern werden Begriffe mit dem dekodierten Text verglichen: Numerische Referenzen, die benannten Referenzen von HTML 4.01, ' und die Großbuchstaben-Aliase &, ©, >, <, " und ® werden vor dem Abgleich dekodiert, sodass ' das ' eines Begriffs trifft. Andere benannte Referenzen bleiben wie geschrieben, und Referenznamen unterscheiden Groß- und Kleinschreibung (&Amp; bleibt wörtlich). In text-Feldern werden Begriffe mit dem Text verglichen, wie er geschrieben ist. Längere Begriffe werden zuerst und ohne Überlappung in Textinhalten und HTML-Attributwerten erkannt. Merge-Tags und ganze Chips haben bei Überlappungen Vorrang. Jeder Treffer gehört zur ID-Folge des Feldes in Quellreihenfolge; erfolgreiches Demaskieren stellt die ursprünglichen Bytes des Begriffs wieder her. Ein fehlender oder geänderter Platzhalter lehnt das Feld mit tags_changed ab und behält den bisherigen Wert. Ein zusätzliches wörtliches Vorkommen eines Begriffs ist normaler Text: Mit ["Cloud"] wird die Quelle Templatical Cloud stores cloud files zu Templatical <x id="1"/> stores cloud files maskiert, und das deutsche Templatical <x id="1"/> speichert Cloud-Dateien wird angenommen, weil jeder Platzhalter genau einmal zurückkommt. TranslationRequest bleibt unverändert.
Übergeben Sie in Headless-Abläufen dasselbe doNotTranslate?: readonly string[] an localeStrings und applyLocaleStrings; auch maskTags(text, syntax, format, { doNotTranslate }) akzeptiert es. Die Helfer trimmen Begriffe beim Abgleich; init() prüft die Größenlimits. Vorhandene Übersetzungen verwenden die Quell-IDs. Fehlt in einer gespeicherten Übersetzung ein geschützter Begriff oder wurde er geändert, bleibt ein ausgewähltes Feld geeignet: Die alte Übersetzung wird nicht gesendet und die Übersetzung beginnt beim aktuellen Standard. Prüfen Sie einen konfigurierten Begriff durch Übersetzen eines Feldes, das ihn enthält: Der Provider erhält einen Platzhalter, und die gespeicherte Übersetzung enthält den ursprünglichen Begriff.
Aufrufe laufen nacheinander. Bei content_too_large halbiert der Editor den Batch und versucht es erneut bis zu einem Feld; lehnt der Provider auch ein einzelnes Feld ab, endet der Durchlauf mit seiner Meldung. Ein einzelnes Feld über der Grenze von 20,000 Einheiten wird nicht gesendet: Der Durchlauf endet an diesem Feld, ohne den Provider aufzurufen, behält die bereits zurückgegebenen Felder, und der Ergebnishinweis meldet „1 Feld ist zu lang zum Übersetzen.“, die eigene Grenze des Editors. Übersetzen Sie dieses Feld von Hand oder tauschen Sie seinen Block mit strings export --block <id> und strings import aus. Vor Abbrechen oder einem Provider-Fehler erhaltene gültige Felder werden gemeinsam als ein Rückgängig-Schritt angewendet. Ein Durchlauf ohne Änderung zeichnet nichts auf. Mit Zusammenarbeit ist dies eine gewöhnliche Bearbeitung; von anderen Sitzungen in der erfassten Zielsprache oder strukturell gesperrte Blöcke werden ausgelassen und gemeldet.
code | Wire-Status | Bedeutung |
|---|---|---|
rate_limited | 429 | Zu viele Anfragen |
limit_reached | 403 | Das Kontingent des Providers ist erschöpft |
content_too_large | 413 | Mit weniger Feldern erneut versuchen |
translation_failed | 502 | Kein gültiges Übersetzungsergebnis |
Lehnen Sie mit verständlicher Nachricht und einem dieser String-code-Werte ab, etwa new SdkError("Translation is unavailable.", 502, "translation_failed") aus @templatical/types. Der Editor erkennt Codes über Bundle-Grenzen hinweg, auch beim eigenen SdkError eines Integrators; dieselbe Klasseninstanz ist nicht nötig. Ein Abbruch lehnt mit AbortError ab. Der editorlokale Code translation_running lehnt einen zweiten parallelen Durchlauf ab; er ist kein TranslationErrorCode und kein Wire-Routenfehler. Ungültige Kit-Anfragen ergeben 422 { message } ohne Code.
HTTP-Protokoll
createHttpTranslationProvider({ url, headers?, fetch? }) verwendet eine Basis-url, optional headers: () => Record<string, string> | Promise<Record<string, string>> und optional fetch: typeof fetch. Dienstschlüssel bleiben im Backend; headers dient der Authentifizierung Ihrer Anwendung.
Der Routenname ist translate: POST /templates/{template}/translate, relativ zu url. Bei templateId: null ist {template} gleich _. Der JSON-Body ist die vollständige TranslationRequest; 200 liefert TranslationResult als JSON. Anfragen und Antworten tragen Templatical-AI-Wire: 1; diese Route verwendet kein SSE und benötigt keinen Sitzungsheader. Fehler verwenden { message, code, ...details } mit dem obigen Status. Cross-Origin-Server erlauben Templatical-AI-Wire, Content-Type und ihren Auth-Header und exponieren Templatical-AI-Wire.
Server-Implementierung
Dieser Fetch-API-Handler benötigt @templatical/ai-kit. Authentifizieren und autorisieren Sie vor dem Aufruf sowohl das Pfad-Template als auch templateId im Body; autorisieren Sie auch Anfragen ohne Template. Übergeben Sie einen AiModelAdapter, etwa einen Adapter aus dem KI-Kit, mit Ihren serverseitigen Modell-Zugangsdaten:
ts
import {
aiErrorResponse,
aiJsonResponse,
readTranslationRequest,
translateFields,
type AiModelAdapter,
} from "@templatical/ai-kit/server";
// Mount at POST /templates/{template}/translate after authentication and authorization.
export async function handleTranslation(
request: Request,
model: AiModelAdapter,
): Promise<Response> {
try {
const body = await readTranslationRequest(request);
const result = await translateFields({
request: body,
model,
signal: request.signal,
});
return aiJsonResponse(result);
} catch (error) {
if (
request.signal.aborted ||
(error instanceof Error && error.name === "AbortError")
)
throw error;
return aiErrorResponse(error);
}
}readTranslationRequest(request) prüft JSON-Body und Aufrufgrenzen; auch readAiRequest erkennt die Route translate. translateFields({ request, model, signal? }) sammelt Text aus model.stream mit tools: []. Es prüft jede angeforderte ID genau einmal und deren Platzhalter-Multimenge, versucht fehlgeschlagene Felder einmal zu reparieren und liefert gültige Felder in Anfragereihenfolge. Abgeschnittene Antworten, Verweigerungen und ungültiges JSON werden einmal repariert; besteht kein Feld die Prüfung, folgt translation_failed. AbortError wird unverändert weitergegeben. Das Kit bereinigt zurückgegebenes HTML nicht.
Ohne das Kit implementieren Sie translate für Ihren Dienst. Gruppieren Sie für DeepL die Felder nach Format, senden Sie tag_handling=html für html-Felder und tag_handling=xml für text-Felder und ordnen Sie die Ergebnisse wieder den Feld-IDs zu. DeepL dokumentiert HTML-Verarbeitung und XML-Verarbeitung (geprüft am 26.10.2026): Die XML-Beispiele erhalten Platzhalter-IDs und leere Elemente, die HTML-Verarbeitung erhält Markup bei toleranter Syntaxanalyse. Diese Einstellungen ersetzen nicht die exakte Platzhalterprüfung des Editors; führen Sie die Konformitätssuite unten gegen Ihren Provider aus.
Mock und Konformität
createMockTranslationProvider({ translations?, delayMs? }) aus @templatical/ai-kit/testing akzeptiert translations?: Record<string, Record<string, string>>, nach Zielsprache und Feld-ID indiziert, und delayMs?: number. Ohne hinterlegte Übersetzung wird [<targetLocale>] vorangestellt, bei HTML im ersten sichtbaren Textsegment; Platzhalter bleiben erhalten. Abbrüche werden berücksichtigt.
Prüfen
Führen Sie dies in Node.js mit installiertem @templatical/ai-kit zunächst gegen den Mock aus und ersetzen Sie ihn anschließend durch Ihren authentifizierten HTTP-Provider:
ts
import { createMockTranslationProvider, runTranslationConformance } from "@templatical/ai-kit/testing";
const checks = await runTranslationConformance(createMockTranslationProvider(), { locale: "de" });
for (const check of checks) {
if (!check.ok) throw new Error(`${check.name}: ${check.detail ?? "failed"}`);
}runTranslationConformance(provider, options?: { locale?: string }) liefert Promise<{ name: string; ok: boolean; detail?: string }[]>; die Zielsprache ist standardmäßig de. Geprüft werden Platzhalter, HTML-Tagnamen (p, strong, b, em, i, u, s, a, br, span), Teilergebnisse, eine Anfrage mit 51 Feldern (mit content_too_large ablehnen oder vollständig übersetzen) und AbortError. Jeder Aufruf hat eine Frist von 5.000 ms. Die Node-Prüfung kontrolliert Tagnamen und ersetzt nicht den DOM-Sanitizer des Editors. Echte Provider können Tokens oder Kontingent verbrauchen.
Speichern Sie nach dem Übersetzen das Template und führen Sie npx -y @templatical/template-tools validate template.json --locale de --json aus. Prüfen Sie die Befunde: Für die übersetzten Felder dürfen weder localization.untranslated, localization.stale noch localization.merge-tags-differ auftreten. Fehlend-/Veraltet-Warnungen allein verursachen keinen Fehler-Exit. Siehe Validierungsvertrag.