Changelog¶
Changelog¶
[1.4.5]¶
Upgrade: no database changes; cache:flush is enough (new JavaScript).
Fixed¶
- Slider content switched back to the first foreign language. The observer that translates nodes added by scripts kept the language it was started with. A looping slider re-appends its slides, so after es → it the slider headings returned to Spanish within seconds (seen live on the start page). Added nodes now follow the language selected last, and a late answer for a language the visitor has already left is cached but no longer written into the page.
[1.4.4]¶
Upgrade: no database changes; cache:flush is enough (new JavaScript).
Fixed¶
- Switching between two foreign languages left segments in the previous one. A segment without a translation for the new target — not cached in cache-only mode (HTTP 429), or a failed request — kept the text it showed, so after es → it uncached headings stayed Spanish between Italian ones. Such segments, attributes and the document title now fall back to the source text. Translations from the browser cache still apply.
Added¶
- Browser test
Tests/JavaScript/dom-switch.cjs(Playwright).
[1.4.3]¶
Upgrade: no database changes; cache:flush is enough. Ukrainian and
Serbian results translated by the LLM before this release are wrong in many
places and stay in the cache, because its key does not include the prompt.
After the update, purge and warm them again:
vendor/bin/typo3 ntlingua:cache-purge --target=uk,sr --model=gpt-4o-mini
vendor/bin/typo3 ntlingua:warmup-dom --target=uk,sr
Leave out --model to purge them regardless of the model (DeepL results
included); --dry-run only counts.
Fixed¶
- The LLM prompt named languages only by code. "Übersetze … von de nach uk"
was read as United Kingdom: on one site about 566 of ~790 cached Ukrainian
entries were English, and Serbian came back in either script. Source and
target language are now named in German, in their own language and by code,
e.g. "nach Ukrainisch (Українська, Sprachcode uk)"; for Serbian the script is
fixed to Cyrillic, matching the menu label "Српски" (also: Bosnian Latin,
Chinese simplified characters). Codes outside the catalog are named via
\Locale::getDisplayLanguage()when intl is installed, otherwise by code. - The loading overlay and the status line were translated as page text.
Both show "Traduction en français …" while a language loads; the next switch
collected that text and sent it for translation — a 429 each time in
cache-only mode. Both now carry
data-nt-notranslate; the status line is marked by the script as well, since sites bring their own switcher markup.
Added¶
ntlingua:cache-purge --target=<codes> [--model=<model>] [--dry-run]removes cached translations of the given target languages, for results that are wrong although the call succeeded.
Changed¶
- One language catalog (
Localization\LanguageCatalog) for the DOM languages of the menu, the choice in the site configuration and the names in the LLM prompt, instead of two copies; it adds the German name of every language.
[1.4.2]¶
Upgrade: no database changes; cache:flush is enough.
Fixed¶
- LLM chunks with a wrong-length answer stayed untranslated for good. The
model merges neighbouring fragments (list items, sentence parts split by a
line break), so the answer for a chunk of 20 strings had fewer elements; the
whole chunk became
nulland the identical resend in the next warmup failed the same way every night (seen on a statute page with 18 segments missing in nearly all target languages). - A chunk whose answer arrived but does not fit — no JSON array, another
length, or an element that is not text — is now halved and the halves are
sent again, until the answer fits or a single string is left. A single
string that still fails stays
nulland setslastError()(e.g. "answer count mismatch (2 for 1)"), so the reason shows in the warmup log. - Only the chunks that miscounted are split; the halves run through
completeMany()when the provider supports it. - Transport failures (exception, empty answer) are not split, so an outage does not multiply the number of requests.
- Each additional request is reserved on its own with the characters of its half. When the budget refuses, splitting stops, translations already received are kept and nothing is thrown.
- An element that is an array no longer reaches a
(string)cast, which raised "Array to string conversion" (an exception in the Development context). - The system prompt now asks to translate every element on its own, sentence fragments and list items included, and never to merge, split or drop elements.
[1.4.1]¶
Upgrade: no database changes; cache:flush is enough.
Fixed¶
- The page uid never reached the browser.
page.jsFooterInlineis rendered throughcObjGetSeparated(), which only takes numeric keys; the named keyntLinguaPageUidwas silently dropped. The DOM translation then answered "invalid page" on every site whose template does not renderdata-page-uiditself. Now rendered under the numeric key 73810 (reported by Stadt Villingen-Schwenningen). lib.ntLinguaExcludehad no effect, for the same reason (now key 73811), and because the widget read the value while loading — before the inline script after it had set it. It is now read when translating, like the page uid.
[1.4.0]¶
Upgrade: no database changes; cache:flush is enough. Two new extension
settings (triggerMode, botProtection), both backwards compatible; bots no
longer trigger Einfache/Leichte Sprache by default.
Findings reported by Stadt Villingen-Schwenningen after going live.
Fixed¶
- Error page on parallel conversions.
TextCacheRepository::put()deleted and re-inserted the entry; when two requests converted the same text at the same time, the second insert hit the unique key and the visitor got an error page. The conflict now updates the entry. - One failing passage broke the whole page. The Einfache-Sprache middleware
and
TransformProcessornow keep a passage in its original wording on any error (logged), instead of only on an exhausted budget. - Single text nodes added by scripts were never translated — the DOM walker only visited the descendants of an added node.
Added¶
- Crawler protection for Einfache/Leichte Sprache. Search and AI crawlers,
HTTP libraries and requests without a browser user agent never trigger a
conversion (
botProtection, on by default); in DOM translation they get cache hits only. A page with?ntlingua=is sent withX-Robots-Tag: noindex, nofollow, a converted page additionally withCache-Control: private, no-store. triggerMode:any(parameter or cookie, as before),cookie(followed?ntlingua=links do nothing) orsigned(only a cookie the widget obtained from the server; it is signed with the encryption key and expires after 30 days). The widget now always fetches the cookie through the API ({"setMode": "einfache"}).- Exclusions by configuration:
lib.ntLinguaExclude.value = .social-wall, #chat(CSS selectors) keeps areas out of the DOM translation, in addition totranslate="no"in the markup. - Cache hits in the log: one row per day and language (
action = cache,chars_incharacters,chars_outsegments).
Changed¶
- Fewer DeepL characters. Text is sent with whitespace folded (template indentation had made up about 42 % of the billed characters at the reporting site); the node keeps its own outer whitespace. Text without letters (phone numbers, dates, page numbers) is not sent. The log now records the characters actually sent.
- Content loaded later (sliders, lazy loading) is collected for 150 ms and sent in one request; a text already requested is not requested again while the first request runs; copies of translated nodes (slider clones) are recognised as known translations and not sent as "German" source again.
[1.3.0]¶
Fixed¶
- Ein fehlgeschlagener Übersetzungsversuch wird nicht mehr als Ergebnis
zwischengespeichert. Lieferte ein Chunk keine oder eine unpassend lange
Antwort, gab
LlmTranslatordafür den Quelltext zurück;DeepLTranslatortat dasselbe bei fehlendem Schlüssel und bei abweichender Trefferzahl. Für den Aufrufer war das von einer korrekten Übersetzung nicht zu unterscheiden — er schrieb den deutschen Originaltext in den Cache, und von da an beantwortete der Cache jede weitere Anfrage damit. Der Versuch wurde nie wiederholt, die Stelle blieb dauerhaft deutsch. Auf einer betroffenen Installation waren das 300 Segmente: ganze Absätze auf Türkisch und Arabisch, auf Französisch die gesamte Hauptnavigation. Besonders tückisch, weil der Endpunkt Erfolg meldete und die Seite vollständig aussah.
Das ergänzt die mit 1.2.0 eingeführte $partial-Behandlung: Jene erkennt an
einem unverändert zurückgekommenen Text, dass er nach einer Budget-Ablehnung
vermutlich nie gesendet wurde. Ein gescheiterter oder unpassend langer Chunk
wird jetzt nicht mehr vermutet, sondern vom Übersetzer als solcher gemeldet.
- Die Extension lief auf TYPO3 12.4 und 13.4 überhaupt nicht. Der EventListener
ProtectSourceTermsgriff mitgetContent()/setContent()auf Methoden zu, dieAfterCacheableContentIsGeneratedEventerst in TYPO3 14 hat. Auf einer 13.4 brach damit jede Seitenauslieferung mit „Call to undefined method" ab, sobald die Extension installiert war — obwohlcomposer.json^12.4 || ^13.4 || ^14.0angibt. Der Listener unterscheidet die Fälle jetzt und holt den Inhalt auf 12.4/13.4 über denTypoScriptFrontendController.
Added¶
- Ein Übersetzungsausfall ist von außen erkennbar. Bisher antwortete
/nt-lingua/transformauch bei vollständigem Ausfall mit200und lieferte die Quelltexte aus — weder eine Überwachung noch das Log im Backend konnten die Störung bemerken. Die Antwort enthält nununtranslatedunddegraded, dazu die KopfzeileX-NtLingua-Untranslatedfür eine Prüfung ohne JSON-Auswertung. Ein vollständiger Ausfall antwortet mit503, ein Teilausfall bleibt200— der übersetzte Teil ist ja brauchbar. Für die Anzeige ändert sich nichts: Das Client-Skript wertet den Status nicht aus, die Seite bleibt in der Ausgangssprache. Jeder Ausfall wird zusätzlich im Übersetzungs-Log vermerkt. - Der Log nennt den Grund eines Ausfalls.
LlmTranslatorfing jede Ausnahme des Anbieters ab und verwarf sie; im Log stand danach nur, dass Segmente fehlen. Ein nicht hinterlegter Schlüssel, ein widerrufener Schlüssel und ein nicht erreichbarer Anbieter sahen gleich aus. Die Meldung wird jetzt an den Log-Eintrag angehängt, etwa „(Anthropic API key is not configured.)". In die API-Antwort gelangt sie bewusst nicht — Anbietermeldungen sind für den Betreiber, nicht für anonyme Aufrufer. TextTransformationService::translateStrings()gibt die Anzahl über den optionalen Parameter&$untranslatedzurück. Bestehende Aufrufe bleiben gültig.
Changed¶
TranslatorInterface::translateBatchmeldet ein nicht übersetztes Segment alsnullstatt als Quelltext. Der Quelltext wird weiterhin ausgeliefert — Seite und Datensatz bleiben vollständig —, aber er wird nicht gespeichert, sodass der nächste Aufruf es erneut versucht. Eigene Implementierungen der Schnittstelle müssen angepasst werden.- Bereits zwischengespeicherte Fehlschläge lassen sich einmalig entfernen mit
DELETE FROM tx_ntlingua_text_cache WHERE result_text = source_text;
[1.2.1]¶
Fixed¶
%env(NAME)%for the DeepL API key now works. The documentation has listed the placeholder fordeepLApiKey, but the value was never resolved: the placeholder text itself was sent to DeepL as the key, every request was rejected and translation fell back to the original text. The placeholder is now replaced by the environment variable. A variable that is not set counts as "no key configured" (LLM translator is used) instead of producing failing requests. Keys entered directly keep working; surrounding whitespace is trimmed.
[1.2.0]¶
Security¶
- Every resend of a provider request is paid from the public budget.
TextTransformationServicereserved the budget once before handing a batch to the translator.LlmTranslatorsplits batches into chunks of 20, sends them concurrently and resends every chunk whose answer came back empty — those resends went out without a reservation (an in-memory reproduction: 21 strings produced two concurrent calls plus two unbudgeted resends). The same applied to DeepL's resends after HTTP 429 and to the LLM fallback after DeepL HTTP 456. The budget is now passed down to the translators (BudgetAwareTranslatorInterface), which reserve right before each send — every chunk, every resend. Without budget left nothing is resent (and DeepL does not wait out its back-off); entries left untranslated are neither returned nor cached. Direct LLM use and the DeepL fallback both stay within the per-client and global caps; cache hits still cost nothing. Backend, CLI and scheduler pass no budget and keep the previous behaviour. - Limit for concurrently running public provider requests. Character
budgets capped the cost but not how many workers and provider connections
anonymous requests could tie up at once. A public request now takes one of
maxConcurrentProviderRequestsinstallation-wide slots on its first reservation and frees it when it is done. Slots are rows intx_ntlingua_ratelimit, taken only by a conditionalUPDATE(or theINSERTcreating them), and expire afterconcurrencySlotTimeoutseconds so a crashed request cannot block a slot for good. When all slots are taken the API answers HTTP 503 withRetry-Afterand still returns cache hits; the GET path keeps the original wording. Requests served entirely from the cache never need a slot. Race test against MariaDB with 30–40 parallel processes: exactly the configured number of slots granted, every run.
Added¶
- Extension settings
maxConcurrentProviderRequests(default 4,0= no limit) andconcurrencySlotTimeout(default 120 seconds). BudgetAwareTranslatorInterfacefor translators that send more than once per batch; plainTranslatorInterfaceimplementations keep working and are reserved once per batch.- Tests for budgeted resends (direct LLM and DeepL fallback, with and without
budget left, global caps, cache hits), DeepL 429 resends, the concurrency
slots and the 503 answer; race test
Tests/Concurrency/concurrency-slots-race.php.
Changed¶
BudgetExceededExceptionis no longer final;ConcurrencyLimitExceededExceptionextends it so all existing refusal paths apply.PublicUsageLimits::budgetFor()returnsRateLimitedProviderBudget; callers callrelease()when their provider work is done (the destructor does it as a fallback).
Upgrade notes¶
- No schema change: the slots use the existing table
tx_ntlingua_ratelimit. Flush caches after the update (new classes and settings). - Accepted limitation, unchanged:
domTranslationCacheOnlystays off by default. As long as it is off, DOM translation translates arbitrary rendered text within the budgets, and a caller can use up the global budget for all visitors until the window ends. Budgets and slots bound cost and load; they do not bind the text to the site's content. This is documented as an accepted limitation, not as a fix.
[1.1.0]¶
Security¶
- The transform API honours page visibility.
POST /nt-lingua/transformloadedtt_contentfields by uid with frontend restrictions on the record only. A visible element on a hidden, scheduled or group-protected page, in a branch closed with "extend to subpages", on a folder or in another site could be read and sent to the provider. The newContentAccessGuardapplies the frontend's own rules (PageRepository,RootlineUtility,RecordAccessVoter) to the page and its rootline, checks site and language, and runs before any text reaches a provider. The middleware now runs after frontend authentication so group access of logged-in users keeps working. - Provider answers can no longer inject markup. Whether an answer was
sanitized depended on the source containing
<; for plain sources it was only entity-decoded — encoded markup became real markup — and then inserted viainnerHTML(field mode) or parsed as HTML (?ntlingua=GET path). Now: HTML sources are always sanitized against the allowlist, plain sources yield plain text with tags removed, the API states the outputformat,nt-lingua.jsassigns text viatextContent, and the GET path escapes the model's text before restoring the page's own tags. Existing cache entries are sanitized again on every output instead of being invalidated — no provider cost, and safe entries come out unchanged. - Cost limits cover every public provider call and hold under parallel
load. The GET path (
?ntlingua=einfache|leichteor cookie) and theTransformProcessorran without any limit; the field mode only estimated costs. A budget is now reserved right before each provider call — POST, GET, and the LLM fallback after DeepL HTTP 456 — with a per-client allowance and new installation-wide hourly and daily caps that cannot be bypassed by switching IP addresses. IPv6 clients are counted per /64. The counters moved from a read-compare-write cache entry to the tabletx_ntlingua_ratelimit, updated by a single conditionalUPDATE; in a test with 20 parallel processes the old logic granted 160–487 reservations against a limit of 100, the new one exactly 100. - DOM translation is bound to a page of the site. Requests must carry the
uid of a page the visitor can open in the current site (
pageUid, sent bynt-lingua.jsautomatically); otherwise HTTP 400.
Added¶
- Extension settings
rateLimitGlobalCharsPerHour(default 500000) andrateLimitGlobalCharsPerDay(default 2000000). - Extension setting
domTranslationCacheOnly(default off): public DOM translation is served from the cache only, new segments are translated by the DOM warmup task. This is the only mode in which anonymous callers cannot have arbitrary text translated. - Functional test suite (SQLite, in-process fake providers) and a race test for
the counters against MariaDB (
Tests/Concurrency/rate-limiter-race.php).
Changed¶
- Exhausted budgets answer with HTTP 429 but still return cache hits; the GET path keeps paragraphs it cannot simplify in their original wording.
- Field mode accepts at most 100 items per request (was 600).
Upgrade notes¶
- Run
vendor/bin/typo3 extension:setup(createstx_ntlingua_ratelimit) and flush caches. The former cachentlingua_ratelimitis no longer used; itscache_ntlingua_ratelimit*tables can be dropped via the database analyzer. - Known limitation: DOM translation translates rendered segments that do not
exist as records, so without
domTranslationCacheOnlyarbitrary text can still be translated within the budgets. Pages that echo user input inside<main>(e.g. search results) should mark that area withdata-nt-notranslate.
[1.0.21]¶
Fixed¶
- A failing translator no longer aborts saving the record. The DataHandler
hook wrapped its translation loop in
try … finallywith nocatch, so any exception from the translator escaped into DataHandler and ended the whole write. On live this surfaced as a403 Forbiddenfrom DeepL (revoked key) that broke animpexp:importand left half-created records behind, but it applies to every outage — exhausted quota, network failure, provider down. A translation that does not happen is a missing translation, never a reason to lose the record: the failure is now logged, reported to the editor as a flash message, and saving continues. - An invalid API key is reported as such. DeepL answers
401/403when a key is revoked, expired or disabled. Instead of passing the raw HTTP error on, the translator now raises a message naming the cause and the remedy. These codes are deliberately not retried — a second attempt does not make a key valid; only429(throttling) is retried,456remains the quota exception.
Added¶
- Extension setting
autoTranslateOnSave(default on) to switch off translate-on-save entirely. Bulk operations — imports, migrations, mass editing — write hundreds of records, where one translation request per record is slow and burns quota. Until now the only way out was the per-record fieldtx_ntlingua_exclude. The per-site toggle in the site configuration keeps working as before; this switch sits above it.
[1.0.20]¶
Fixed¶
- Interne Links überlebten die Übersetzung nicht. RTE-Inhalte speichern
Verweise als
t3://page?uid=…. Der HTML-Sanitizer, der die Antwort des Übersetzungsanbieters säubert, lässt inhrefnur http, https, mailto, tel und lokale Pfade zu — jedes andere Schema verliert das Attribut. Aus<a href="t3://page?uid=508">Ziel</a>wurde damit in jeder Übersetzung<a>Ziel</a>: Der Text stand da, das Ziel war weg. Betroffen waren native Overlays ebenso wie Einfache/Leichte Sprache — unbemerkt, weil ein Link ohnehrefim Frontend wie Fließtext aussieht.
Der URI wird jetzt vor dem Übersetzen auf eine reservierte .invalid-Adresse
abgebildet und danach zurückgesetzt (Typo3UriProtector). Der Sanitizer
bleibt eng eingestellt — er säubert fremde Ausgabe und sollte keine
zusätzlichen Schemata erlauben. Nebeneffekt: Der Anbieter bekommt den URI
URL-kodiert und übersetzt keine Wörter mehr darin.
Bereits zwischengespeicherte Übersetzungen aus der Zeit davor werden erkannt und verworfen, statt weiter ausgeliefert zu werden: Enthält die Quelle mehr verlinkte Anker als der Cache-Treffer, wird neu übersetzt.
[1.0.19]¶
Fixed¶
- Batch-Übersetzungs-Modul zeigte Zielsprachen mehrfach (z. B. „English (en)"
dreimal):
getTranslationLanguages()sammelte die Sprachen über ALLE Sites (getAllSites()), sodass in einer Multi-Site-Installation dieselbe Sprach-ID je Site einmal erschien. Die Liste wird jetzt nach Sprach-ID dedupliziert — jede Zielsprache erscheint genau einmal.
[1.0.18]¶
Added¶
- Öffentliche API
NtLingua.translateNode(node): übersetzt einen dynamisch gerenderten Teilbaum (z. B. die Suchergebnisse von nt-search) in die aktuell aktive DOM-Sprache; no-op ohne aktive Sprache.
Fixed¶
- Leerzeichen an Inline-Element-Grenzen blieben erhalten: Der Server trimmt
Segmente — bei Textknoten neben
<strong>/<mark>u. ä. verschwand das Leerzeichen an der Naht („TYPO3 Hosting" → „TYPO3Hébergement").apply()stellt führendes/abschließendes Whitespace des Quellknotens wieder her.
[1.0.17]¶
Fixed¶
ntlingua:warmup-dom: Bei leerem Site-Feldnt_lingua_dom_languagesgreift jetzt derselbe Fallback wie im Frontend (allemode:'dom'-Einträge ausLanguages.php) — vorher wurde die Site übersprungen, obwohl die Sprachbuttons angezeigt werden.
[1.0.16]¶
Added¶
- Neuer CLI-Command
ntlingua:warmup-dom(Scheduler-fähig): wärmt den DOM-Übersetzungs-Cache mit den echten Frontend-Segmenten — er rendert jede Seite aus der XML-Sitemap wie ein Besucher und extrahiert Textknoten +title/alt/placeholder/aria-label+ Seitentitel nach denselben Regeln wient-lingua.js collect()(inkl.translate="no"-/notranslate-Ausschluss). Damit treffen Browser-Anfragen den Cache wirklich — der bestehendentlingua:warmupwärmt nur rohett_content-Felder und erzeugt für den DOM-Pfad unbrauchbare Schlüssel (andere Granularität, Navigation/Footer/ ContentBlocks fehlen). Optionen:--target(Standard: Site-Feldnt_lingua_dom_languages),--site,--sitemap(explizite URL bei Redirect-Setups),--limit,--sleep,--dry-run. Bereits gecachte Segmente kosten nichts — geplante Läufe übersetzen nur Neues/Geändertes.
[1.0.15]¶
Added¶
- DOM-Übersetzung: Der Seitentitel (
document.title) wird jetzt mitübersetzt — Browser-Tab und Verlaufseintrag erscheinen in der gewählten Sprache; beim Zurücksetzen wird der Originaltitel wiederhergestellt. Läuft über denselben Cache wie die übrigen Segmente (Server + Client), kostet also nach dem ersten Mal keine weiteren Tokens.
[1.0.14]¶
Changed¶
- DOM-Übersetzung: Das Fortschritts-Overlay („Übersetze …") erscheint jetzt in der
Zielsprache (fr/es/it/hu/en/pt/nl) statt fest auf Deutsch. Fallback auf die
deutsche Phrase mit Sprachnamen für nicht gelistete Codes.
einfache/leichtebleiben auf Deutsch.
[1.0.13]¶
Fixed¶
- DOM-Übersetzung: Kaskaden-Bug behoben. Ein Wechsel Sprache A → B sammelte den
bereits übersetzten DOM (Sprache A) und schickte ihn als Quelle → jeder
Sprache-zu-Sprache-Wechsel war ein garantierter Cache-Miss (A→B war nie
gewärmt) und traf live das LLM (8–10 s je Umschaltung). Es wird jetzt immer aus
dem gespeicherten deutschen Original übersetzt (
origOf()statt des aktuell angezeigten Textes).
[1.0.12]¶
Added¶
- Client-Cache (localStorage) pro Zielsprache, Schlüssel = Quelltext (nicht URL): geteilte Chrome (Navigation/Footer) und wiederkehrende Inhalte werden einmal für die ganze Site gecacht und bei jeder Folgeseite ohne Roundtrip angewandt. Selbstinvalidierend (Schlüssel = Quelltext) plus Deploy-Stempel und 14-Tage-TTL.
- LLM-Fallback: Der Batch wird in 20er-Chunks zerlegt und — sofern der Provider es
unterstützt — nebenläufig ausgeführt (nt-ai
completeMany()via curl_multi). Rund 2× schneller.
Fixed¶
- LLM-Fallback: robuster Per-Chunk-Fallback statt „all or nothing". Bisher wurde bei einer Zähl-Diskrepanz der GESAMTE Batch als Originaltext zurückgegeben und so unübersetzt gecacht (ganze Sprachen „vergiftet", z. B. Ungarisch). Jetzt betrifft ein Fehler nur einen 20er-Chunk, mit sequentiellem Retry pro leerem Slot.
- Whitespace-Trim der LLM-Ausgabe — kein
\n…-Müll mehr im Cache. normalize()faltet jetzt auch nbsp (U+00A0) und weitere Unicode-Leerzeichen;\smatcht in PCRE nur ASCII → sonst entstanden Fast-Duplikate im Cache. Beidenormalize()-Kopien (TextTransformationService + GlossaryRepository) angeglichen.
[1.0.11]¶
Fixed¶
- Performance:
translateStrings()bündelt die Cache-Lookups in einen Batch-Query (TextCacheRepository::getMany(),content_hash IN (…)) statt einem Query pro Segment. Gegen die entfernte Live-DB waren das hunderte sequentielle Roundtrips → sekundenlanger, „hängender" Spinner bei Volltext-Übersetzung (128 gecachte Segmente: 12 s → ~1 s).
[1.0.10]¶
Fixed¶
- Glossar:
rootLevel = -1ergänzt — Einträge lassen sich jetzt auch auf Root-Level (pid=0) anlegen, nicht nur auf Seiten.
[1.0.9]¶
Fixed¶
- Container-Übersetzung (Korrektur zu 1.0.8): Das
tx_container_parentder Kind-Overlays zeigt jetzt auf das Default-Sprach-Container-Record statt auf das übersetzte Container-Overlay — so verlangt es b13/container im Connected-Mode (vgl.container:fixContainerParentForConnectedMode). 1.0.8 hatte auf das Overlay gezeigt, was pro Kind einenChildInTranslatedContainerErrorund im Backend „doppelte/verwaiste" Elemente erzeugte. Frontend war in beiden Fällen ok.
[1.0.8]¶
Fixed¶
- Native Übersetzung von Seiten mit EXT:container-Struktur (Spalten/Grids):
Container werden jetzt in Hierarchie-Reihenfolge lokalisiert (Container vor
Kindern) und
tx_container_parentder Overlays auf die Container-Overlays umgebogen. Vorher blieben die Kind-Overlays verwaist (colPos ohne Container → „unbenutzte Elemente"), Spalten/Bilder fehlten und beim Lokalisieren eines Kindes vor seinem Container trat eine Exception auf. - Glossar-Records lassen sich jetzt auf jedem Seitentyp anlegen
(
ctrl.security.ignorePageTypeRestriction), inkl. Root-Seite. Zielsprache-Feld mit Hinweis ergänzt, dass*für alle Sprachen gilt. Doku: Ablageort istpid-unabhängig (global gelesen).
[1.0.7]¶
Fixed¶
- Einfache/Leichte Sprache: Inline-HTML (
<img>,<span>,<a>…) in Textblöcken wurde nach der KI-Umschreibung als literaler Text ausgegeben. Inline-Markup wird jetzt vor dem KI-Call durch Platzhalter-Token geschützt und danach 1:1 wieder eingesetzt (analog zum DeepL-Pfadtag_handling=html).
[1.0.6]¶
Fixed¶
- DOM-Übersetzung: Der Transform-Endpoint akzeptiert jetzt die im Site-Setting
nt_lingua_dom_languageskonfigurierten Sprachen (nicht nur die ausLanguages.php).
[Unreleased]¶
Added¶
- Initial release