Entwicklerhandbuch¶
Architektur¶
Frontend-Request
│
├─ POST /nt-lingua/transform ─→ TransformApiMiddleware
│ │
│ ├─ isLanguageTarget(target) = true ─→ TextTransformationService::translateStrings()
│ └─ isLanguageTarget(target) = false ─→ TextTransformationService::transformField()
│
└─ GET Seitenaufruf (Cookie/Param)
└─ TransformProcessor (DataProcessing) ─→ transformField() [Server-Fallback]
TextTransformationService
├─ einfache / leichte ─→ AiProviderInterface (nt_ai) + Prompts
└─ Sprachcodes ─→ TranslatorInterface (LLM/DeepL) + Glossar
TextCacheRepository
└─ tx_ntlingua_text_cache (UNIQUE: content_hash + target)
Zwei Kopplungspunkte:
- Frontend → Server:
POST /nt-lingua/transformwird vonTransformApiMiddlewareverarbeitet - Server → KI/Übersetzung:
TranslatorInterface(LLM oder DeepL) +AiProviderInterface(nt-ai)
nt_lingua kennt keine konkreten KI-Provider — der einzige externe Kopplungspunkt ist Netthinks\NtAi\Provider\AiProviderInterface.
Datenmodell¶
tx_ntlingua_text_cache¶
| Spalte | Typ | Beschreibung |
|---|---|---|
content_hash |
varchar(64) | sha256(normalize(source) \| target \| model) |
target |
varchar(20) | einfache, leichte oder Sprachcode (z. B. en) |
source_lang |
varchar(10) | Quellsprache (Standard: de) |
source_text |
mediumtext | Ursprünglicher Text |
result_text |
mediumtext | Transformierter/übersetzter Text |
provider |
varchar(30) | KI-Anbieter oder deepl |
model |
varchar(80) | Verwendetes Modell |
tokens_used |
int | Verbrauchte Token |
readability |
decimal(5,2) | Ø Wörter/Satz (niedriger = einfacher) |
source_uid |
int | UID des Quell-Datensatzes |
source_table |
varchar(80) | Tabelle des Quell-Datensatzes |
source_field |
varchar(80) | Feld des Quell-Datensatzes |
crdate / tstamp |
int | Unix-Timestamps |
Unique-Key: (content_hash, target) — Einfache, Leichte und alle Sprachcodes teilen sich dieselbe Tabelle kollisionsfrei.
Cache-Invalidierung: AfterDatabaseOperationsEvent → InvalidateTextCache löscht alle Einträge zum betroffenen Datensatz, wenn header oder bodytext gespeichert werden.
tx_ntlingua_glossary¶
| Spalte | Beschreibung |
|---|---|
source_term |
Quellbegriff (vor Vergleich normalisiert) |
target_lang |
Zielsprache (ISO-Code) oder * für alle Sprachen |
target_term |
Festgelegte Übersetzung (bei ignore-Modus leer lassen) |
mode |
fixed (erzwingen) oder ignore (unverändert lassen) |
Glossar-Einträge werden vor Cache und Provider ausgewertet. target_lang='*' gilt sprachunabhängig für alle DOM-Übersetzungen.
tt_content / pages (Erweiterungsfelder)¶
| Feld | Beschreibung |
|---|---|
tx_ntlingua_mt |
1 = maschinell übersetzt (readonly im Backend) |
tx_ntlingua_srchash |
Hash der Quellfelder zum Zeitpunkt der Overlay-Erzeugung — ermöglicht Delta-Erkennung |
CLI-Referenz¶
ntlingua:warmup¶
| Option | Standard | Beschreibung |
|---|---|---|
--target |
einfache |
einfache, leichte oder ISO-Sprachcode |
--limit |
0 |
Max. Datensätze (0 = alle) |
Verarbeitet alle tt_content-Datensätze mit sys_language_uid=0, deleted=0, hidden=0. Cache-Hits werden übersprungen — der Befehl ist idempotent und kann als nächtlicher Scheduler-Task ausgeführt werden.
# Einfache Sprache für alle Content-Elemente vorwärmen:
vendor/bin/typo3 ntlingua:warmup --target=einfache
# Englische Übersetzung, maximal 500 Datensätze:
vendor/bin/typo3 ntlingua:warmup --target=en --limit=500
ntlingua:overlays¶
| Option | Beschreibung |
|---|---|
--language |
sys_language_uid der Zielsprache (muss in TYPO3-Site-Konfiguration vorhanden sein) |
--code |
ISO-Sprachcode (z. B. en, fr) |
Erzeugt verbundene l10n-Overlays (connected mode). Bei Wiederholung werden nur geänderte Datensätze verarbeitet (Delta-Erkennung via tx_ntlingua_srchash).
Übersetzte Felder:
| Tabelle | Felder |
|---|---|
pages |
title, nav_title, subtitle, seo_title, description |
tt_content |
header, subheader, bodytext |
Warning
ntlingua:overlays im Live-Workspace ausführen, nicht in Draft-Workspaces.
JavaScript-API¶
// Sprache oder Modus wechseln:
NtLingua.transform('fr', buttonElement); // DOM-Übersetzung
NtLingua.transform('einfache', btn); // Einfache Sprache
NtLingua.transform('de'); // Reset zur Ausgangssprache
// Aktuellen Zustand abfragen:
NtLingua.getCurrent(); // z. B. 'de', 'fr', 'einfache'
Custom Events¶
document.addEventListener('NtLingua:ready', (e) => {
console.log('Initialisiert, aktives Ziel:', e.detail.target);
});
document.addEventListener('NtLingua:done', (e) => {
console.log('Umschaltung abgeschlossen:', e.detail.target);
});
Eigenen Übersetzer hinzufügen¶
TranslatorInterface implementieren und in Services.yaml als Alias registrieren:
namespace MyVendor\MyExt\Translator;
use Netthinks\NtLingua\Translator\TranslatorInterface;
final class MyTranslator implements TranslatorInterface
{
public function translateBatch(array $texts, string $sourceLang, string $targetLang): array { … }
public function getName(): string { return 'my-translator'; }
public function getModel(): string { return 'my-model'; }
}
# Services.yaml der eigenen Extension:
Netthinks\NtLingua\Translator\TranslatorInterface:
alias: MyVendor\MyExt\Translator\MyTranslator
Fluid-Wrapper (Pflicht für JS-Feldtausch)¶
Content-Felder müssen in FSC-Template-Overrides mit data-ntlingua-*-Attributen umschlossen werden:
<!-- bodytext (z. B. in einem EXT:fluid_styled_content Override): -->
<div data-ntlingua-uid="{data.uid}" data-ntlingua-field="bodytext">
<f:format.html>{data.bodytext}</f:format.html>
</div>
<!-- header: -->
<div data-ntlingua-uid="{data.uid}" data-ntlingua-field="header">
{data.header}
</div>
Fluid-Overrides unter packages/netthinks/Resources/Private/Extensions/fluid_styled_content/ anlegen.
Barrierefreiheit¶
<html lang>unddirwerden bei jedem Umschalten gesetzt (WCAG 3.1.1 / 3.1.2)- Sprachnamen als Autonyme mit
lang-Attribut je Button/Link - Dropdown trägt
data-nt-notranslate— Sprachnamen werden beim Übersetzen der Seite nicht selbst übersetzt role="status"/aria-live="polite"für Fehlermeldungen im Navbar-Bereicharia-live="polite"auf dem Disclaimer-Banneraria-busy="true"auf<main>während der Übersetzung (Overlay-Spinner)aria-pressedauf Buttons für den aktiven Zustand; native Sprachen als Links mitaria-current- Native Sprachen als Links (semantisch korrekt, ohne JS nutzbar, kein WCAG-3.2.2-Problem)
- DOM-Sprachen und Vereinfachung als Buttons (kein Seitenaufruf, reines Client-Verhalten)
data-ntlingua-nativeauf nativen Links → localStorage-Cleanup vor der Navigation- Voll tastaturbedienbar, sichtbarer Fokus; Widget schließt bei Escape
Bekannte Eigenheiten¶
PHP: Numerische String-Keys in Arrays¶
PHP konvertiert Array-Keys, die wie Ganzzahlen aussehen ("1999", "42"), automatisch zu int. In TextTransformationService::translateStrings() kann das im foreach ($missing as $orig => $_)-Loop dazu führen, dass $orig ein int ist, obwohl normalize(string $s) einen string erwartet. Fix: $orig = (string)$orig; am Beginn des Loops.
DeepL: source_lang akzeptiert keine Locale-Varianten¶
DeepLs source_lang-Parameter akzeptiert nur Basiscodes (EN, DE), keine Locale-Varianten (EN-US, DE-AT). Das <html lang>-Attribut kann Locale-Varianten enthalten (z. B. en-US bei locale: en-US in der TYPO3-Site-Konfiguration). DeepLTranslator kürzt den Source-Code mit explode('-', $sourceLang)[0].
Chrome Autotranslate blockiert den TreeWalker¶
Chromes eingebauter Übersetzer setzt class="notranslate" auf das <html>-Element. Die ignored()-Funktion stoppt explizit bei document.documentElement und prüft das <html>-Element selbst nicht — andernfalls würde der gesamte Seiteninhalt als ausgeschlossen gewertet und der TreeWalker lieferte einen leeren Text-Pool.
Zwei Sprachwähler-Instanzen (Mobile + Desktop)¶
Das Partial wird zweimal gerendert (Mobile und Desktop, CSS-gesteuert). setToggleLabel() nutzt querySelectorAll('.nt-lingua-active-label'), um beide Toggle-Buttons synchron zu halten.