Architektur
Diese Seite gibt einen technischen Überblick für die Agenturen, die die Erweiterung warten, erweitern oder anbinden.
Zielplattform: Shopware ~6.6.9 || ~6.7.0, PHP >= 8.2, Vue-3-Administration.
Hinweis
Diese Seite beschreibt die Erweiterung, die in Ihrer Shopware-Installation läuft. Diese Dokumentation beschreibt den Managed-Übersetzungsdienst nur so, wie Händler ihn sehen: die Tarife, das Guthaben, die Servicestufen und das Kontingent. Siehe modernice All-in-One.
Konstanten
| Angabe | Wert |
|---|---|
| Plugin-Klasse | Nice\Translate\NiceTranslate |
| Technischer Name | NiceTranslate |
| Composer-Paket | modernice/sw-translate (Typ shopware-platform-plugin) |
| Namespace-Wurzel | Nice\Translate\ → src/ (PSR-4) |
| Versionskonstante | NiceTranslate::VERSION |
| PHP-Anforderung | >=8.2 |
| Shopware-Anforderung | ~6.6.9 || ~6.7.0 |
| Tabellenpräfix in der Datenbank | nice_translate_ |
| System-Config-Domain | NiceTranslate.config. |
| Präfix der API-Routen | /api/_action/nice-translate/ |
| Präfix der API-Routennamen | api.action.nice_translate. |
| ACL-Schlüssel | nice_translate (Rollen viewer, editor) |
| Service-Tag für Anbieter | nice_translate.provider |
| Administrationsmodul | nice-translate |
Es gibt keine config.xml. Jede Einstellung liegt in der Domain NiceTranslate.config.. Die eigene Einstellungsseite der Erweiterung schreibt die Einstellungen, und der PHP-Code liest sie mit im Code hinterlegten Standardwerten. Die Einstellungsreferenz enthält die vollständige Liste der Schlüssel.
Fünf Dateien enthalten die Service-Verdrahtung, und src/Resources/config/services.xml importiert alle davon: entity.xml, provider.xml, engine.xml, job.xml und api.xml. src/Resources/config/routes.xml lädt die Controller per Attribut:
<import resource="../../Api/**/*Controller.php" type="attribute"/>Komponentenüberblick
Administration (Vue module `nice-translate`)
│ REST /api/_action/nice-translate/*
▼
API controllers (Nice\Translate\Api)
│
▼
JobService ──▶ Message queue (low priority) ──▶ JobStartHandler ──▶ TranslateBatchHandler ×N
│
▼
EntityTranslator (Engine)
│ PlaceholderGuard
│ GlossaryApplier
│ Slot/CustomFields/Snippet walkers
│ RecordTracker (hashes)
│
▼
ProviderRegistry ──▶ TranslationProviderInterface
(DeepL, Google, OpenAI,
Anthropic, Gemini, Mistral,
Managed)Die Erweiterung schreibt die übersetzten Inhalte über das Repository der jeweiligen Zielentität, in die nativen *_translation-Tabellen von Shopware. Eine eigene Kopie einer Übersetzung hält sie nicht vor.
Ablauf einer Anfrage
Ein manueller Übersetzungslauf durchläuft diese Schritte. Jeder Schritt committet, bevor der nächste beginnt. Stirbt ein Prozess während des Laufs, findet die Wiederherstellung deshalb einen Zustand, an dem sie ansetzen kann.
Von der Administration zur API. Das Modul ruft die Admin-API über
NiceTranslateApiServiceauf (apiEndpoint = '_action/nice-translate'). Ein ACL-Schutz sichert jede Route. Siehe Admin-API.JobController::create()zuJobService::create().JobConfig::fromArray()normalisiert dasconfig-Objekt aus dem Request-Body. Die Validierung prüft danach drei Dinge: ob der Anbieter bekannt und konfiguriert ist, ob sich die Zielsprachen auflösen lassen und ob die Erweiterung die Entitäten unterstützt. Die Erweiterung legt eine Zeile innice_translate_jobmit dem Statusqueuedan und versendetJobStartMessage.Wenn der Messenger-Versand eine Exception wirft, liefert der Aufruf trotzdem
200mit der Auftrags-ID. Die committetequeued-Zeile ist die dauerhafte Startmarkierung, und die geplante Wiederherstellung stellt die Nachricht nachträglich zu.JobStartHandler. Eine Transaktion erledigt vier Dinge: Sie ermittelt die betroffenen Entitäts-IDs je Entität und Zielsprache, teilt sie in Batches der konfiguriertenbatchSizeauf, schreibt das vollständige Manifest innice_translate_job_batch, protokolliert die Fehler bei der Ermittlung und setzt den Auftrag mit seiner exaktentotal_items-Zahl aufrunning. Danach hältJobBatchDispatcherein begrenztes Fenster offenerTranslateBatchMessages gefüllt, deshalb flutet ein großer Katalog den Transport nicht.TranslateBatchHandler. Er liest den Auftragsstatus für jede Nachricht erneut, deshalb greift ein Abbruch zwischen zwei Batches. Er nimmt eine verbindungsgebundene Advisory Lock auf die stabile Request-ID des Batches. Existiert eine gespeicherte Quittung, spielt er diese Quittung erneut ein. Andernfalls ruft erEntityTranslator::translateBatch()auf. Die Inhalte und die Quittung committen zusammen. Die Fortschrittsverbuchung erhöht die Zähler und fügt eine Zeile innice_translate_batch_completionein. Nach der Verbuchung des Batches füllt der Dispatcher das Fenster um eins auf.Abschluss. Sobald
processed + failed + skipped >= totalgilt, setztJobProgressUpdater::finish()den Endstatus und schreibt in derselben Transaktion einen unveränderlichen Snapshot innice_translate_finished_event.JobFinishedEventRelayversendetTranslationJobFinishedEventnach dem Commit. Siehe Events & Flow Builder.
Der Finalizer setzt completed oder completed_with_errors und sonst nichts (IF(failed_items > 0, …)). Den Status failed erreicht ein Auftrag nur über JobProgressUpdater::failQueued(). Diese Methode verarbeitet einen queued-Auftrag, dessen Manifest-Vorbereitung fehlschlug. JobService::cancel() setzt cancelled, und dieser Status löst kein Abschluss-Event aus.
Auftragsstatus: queued, running, completed, completed_with_errors, failed, cancelled.
Nachrichten und Handler
Alle drei Nachrichten implementieren LowPriorityMessageInterface von Shopware und transportieren nur skalare IDs und Arrays. Worker können den Kern-Traffic der Queue deshalb vorziehen. Eine echte Trennung der Queue hängt weiterhin von Ihrer Messenger- und Worker-Konfiguration ab.
| Nachricht | Handler | Zweck |
|---|---|---|
JobStartMessage | JobStartHandler | Das Batch-Manifest aufbauen und den Auftrag starten |
TranslateBatchMessage | TranslateBatchHandler | Genau einen Batch ausführen oder erneut einspielen |
RevertJobMessage | RevertJobHandler | Geschütztes, per Keyset paginiertes Zurücksetzen des angewendeten Verlaufs eines Auftrags |
TranslateBatchMessage trägt eine stabile requestId als Idempotenzschlüssel. Eine übergebene, gültige UUID nutzt der Handler unverändert. Andernfalls leitet er die ID deterministisch aus dem unveränderlichen Payload ab, deshalb nutzt jede erneute Zustellung derselben Arbeit denselben Schlüssel. Eine Nachricht mit jobId === null ist ein auftragsloser Batch aus der Automatisierung beim Speichern, und ihr configOverride enthält dann die serialisierte JobConfig.
Die Erweiterung registriert zwei geplante Aufgaben:
| Aufgabe | Aufgabenname | Standardintervall | Der Handler führt aus |
|---|---|---|---|
AutoTranslateTask | nice_translate.auto_translate | 3600 s | Den Verlauf bei jedem Durchlauf aufräumen, danach einen fälligen geplanten Auftrag anlegen |
BatchDispatchTask | nice_translate.dispatch_batches | 60 s | JobBatchDispatcher::recover() und JobFinishedEventRelay::recover() |
AutoTranslateTask läuft stündlich und übersetzt höchstens einmal pro scheduledIntervalHours (Standard 24). Das Aufräumen des Verlaufs geschieht bei jedem Durchlauf. Aufträge entstehen nur, solange scheduledEnabled aktiv ist und das Intervall abgelaufen ist. Events & Flow Builder enthält die vollständige Kette der Prüfungen.
Beide Aufgaben brauchen laufende Worker (messenger:consume für die Transports und scheduled-task:run).
Engine-Pipeline
EntityTranslator::translateBatch(TranslationTask $task): BatchResult ist der anbieterunabhängige Kern. Ein Aufruf verarbeitet genau einen Batch: eine Entität, eine Liste von IDs, eine Zielsprache. Die Stufen laufen in dieser Reihenfolge:
| # | Stufe | Was passiert |
|---|---|---|
| 0 | Frühe Ausstiege | Eine leere ID-Liste liefert ein leeres BatchResult. Die Pseudo-Entität snippet zweigt in den SnippetTranslator ab |
| 1 | Anbieter und Locales auflösen | ProviderRegistry::get(), danach der Shopware-Locale-Code der Ausgangssprache und der Zielsprache |
| 2 | Optionen normalisieren, Felder auflösen | Die Engine führt die Auftragsoptionen mit der Systemkonfiguration zusammen, und TranslatableFieldResolver leitet die FieldSpec-Liste ab |
| 3 | Doppelter Lesevorgang | Einmal im Kontext der Ausgangssprache mit berücksichtigter Vererbung, einmal im rohen Kontext der Zielsprache. Für cms_page ergänzt die Criteria sections.blocks.slots |
| 4 | Fingerabdrücke laden | RecordTracker::getStates() für die Entität, dazu die Config-Zustände der cms_slot-Einträge bei CMS-Seiten |
| 5 | Phase 1: Einheiten sammeln | Die Skip-Gates je Feld entscheiden, welche Werte die Engine sendet |
| 6 | Phase 2: Anbieteraufrufe | Glossar vorbereiten, nach Format gruppieren (html / text), Chunking, Bisektion nach einem Fehler |
| 7 | Phase 3: Nachbearbeitung | Glossar-Ersetzungen, Platzhalter wiederherstellen, Maximallängen durchsetzen |
| 8 | Phase 4: Schreiben | Die Engine setzt die Zusatzfeld- und Slot-Strukturen wieder zusammen und schreibt je Entität innerhalb von JobBatchStore::transactionalResult(), deshalb committen Inhalt und Quittung gemeinsam |
Die Engine wertet die Skip-Gates auf Feldebene aus Stufe 5 in dieser Reihenfolge aus:
mode === 'missing'und es existiert bereits ein direkter Zielwert → überspringen.- Kein erfasster Zustand oder überhaupt kein Zielwert → nicht überspringen.
skipUnchangedund der Hash der Quelle ist unverändert → überspringen.protectManualEditsund der aktuelle Zielwert entspricht nicht mehr dem letzten Wert der Erweiterung → überspringen.
Die Schreibvorgänge laufen über das Repository der Entität, als natives Übersetzungs-Payload, ['id' => …, 'translations' => [$targetLanguageId => [...fields]]], innerhalb des Context-States nice_translate.writing, deshalb löst der Subscriber für das Speichern keine Schleife aus.
PlaceholderGuard ersetzt die Twig-Ausdrücke, die Platzhalter im Stil %name%, {0}, %s, %d und die URLs vor der Übersetzung durch ⟦n⟧-Tokens. Kommt ein Token nicht zurück, wirft die Engine PlaceholderLostException und lässt dieses Feld fehlschlagen. Die Erweiterung schreibt nie beschädigte Inhalte. Schutzmechanismen beschreibt die Details.
Die Werte getMaxBatchSize() und getMaxChunkBytes() des Anbieters begrenzen das Chunking. EntityTranslator::CHUNK_BYTE_LIMIT (80 000 Bytes) deckelt es zusätzlich. Eigene Anbieter beschreibt, wie ein Anbieter daran teilnimmt.
Persistenz
Die Erweiterung besitzt elf Tabellen. Fünf davon haben DAL-Definitionen, die über shopware.entity.definition registriert sind. Die übrigen sechs sind reine DBAL-Aufzeichnungen ohne Repository und ohne Admin-API-Ressource.
| Tabelle | DAL-Entität | Zweck |
|---|---|---|
nice_translate_job | TranslationJobDefinition | Der Auftragskopf: Status, Titel, Konfigurations-JSON, Anbieter, Sprachen, Zähler, Kosten oder Guthaben, Managed-Nutzung und -Kontingent, Zeitstempel |
nice_translate_job_error | JobErrorDefinition | Die Fehler je Datensatz eines Auftrags, mit Fremdschlüssel auf den Auftrag und Cascade Delete |
nice_translate_glossary | GlossaryDefinition | Die Glossarbegriffe: Modus keep oder replace, feste Übersetzungen je Sprache, Groß-/Kleinschreibung, Aktiv-Kennzeichen |
nice_translate_usage | UsageDefinition | Die monatliche Nutzung je Anbieter, eindeutig über (provider_id, period) |
nice_translate_history | TranslationHistoryDefinition | Der vorherige und der neue Feldwert samt Status applied oder reverted für das geschützte Zurücksetzen |
nice_translate_record | — (nur DBAL) | Die sha1-Fingerabdrücke der Quelle und des geschriebenen Ziels, je Entität, Feld und Sprache |
nice_translate_job_batch | — (nur DBAL) | Das dauerhafte Batch-Manifest und die vorübergehende Quittung mit Zählern und Abrechnungsdaten, bis der Fortschritt verbucht ist |
nice_translate_jobless_batch | — (nur DBAL) | Die dauerhafte Batch-Nachricht beim Speichern und ihr Abschluss-Marker |
nice_translate_batch_completion | — (nur DBAL) | Die stabilen Batch-Request-IDs, die die Auftragszähler bereits enthalten |
nice_translate_managed_usage_request | — (nur DBAL) | Die Upstream-Request-IDs des Managed-Dienstes, die die Monatsnutzung bereits enthält |
nice_translate_finished_event | — (nur DBAL) | Die unveränderlichen Snapshots abgeschlossener Aufträge für das Flow-Event-Relay |
Jedes DAL-Feld trägt ApiAware(AdminApiSource::class). Die Entitäten sind deshalb ausschließlich über die Admin-API erreichbar, und nie über die Store-API.
Eine Auftragszeile hält einen schmalen Ausschnitt der Managed-Nutzung fest. ManagedProvider validiert die gesamte Abrechnungsantwort des Upstreams und speichert danach sechs Felder: requestedTier, deliveredTier, multiplier, sourceCharacters, debitedCredits und providerAttempts. Die Kennungen der Route und der Upstream-Modelle, die er dafür geprüft hat, schreibt er nie in die Datenbank. Clients der Administration-API können die Topologie des Managed-Dienstes deshalb nicht beobachten.
Sieben Migrationen erzeugen und erweitern dieses Schema: Basisschema, Übersetzungsverlauf, Managed-Abrechnung, dauerhafte Batches, dauerhaftes Start-Relay, Outbox für Abschluss-Events sowie Batch-Absicherung und Aufräumarbeiten. Keine davon implementiert updateDestructive().
NiceTranslate::uninstall() steigt sofort aus, wenn der Händler die Benutzerdaten behält. Andernfalls entfernt die Methode alle elf Tabellen und löscht die NiceTranslate.%-Zeilen aus system_config. Die Übersetzungen in den nativen *_translation-Tabellen löscht sie nie.
Ausfallsicherheit
Übersetzungsarbeit ist teuer, und bei BYOK-Anbietern ist sie upstream nicht idempotent. Die Fehlerbehebung beschreibt den Betrieb der folgenden Mechanismen.
| Mechanismus | Garantie | Wie |
|---|---|---|
| Dauerhafte Manifeste | Die Arbeitsmenge ist allein aus der Datenbank bekannt, ohne eine Frage an den Transport, was er noch hält | nice_translate_job_batch (und nice_translate_jobless_batch für Arbeit beim Speichern) hält die vollständige Liste der Batches fest, in derselben Transaktion, die den Auftrag auf running setzt. Die Wiederherstellung wartet, bevor sie nie versendete Arbeit übernimmt, stellt unvollständige Versandvorgänge nach einer Sicherheitsfrist erneut zu und rotiert pro Runde einen Batch je Auftrag, deshalb hungert ein großer alter Auftrag neuere Arbeit nicht aus |
| Ausführungsquittungen | Ein Absturz kann weder übersetzten Inhalt ohne die zugehörigen Zähler und die Managed-Nutzung noch Zähler ohne Inhalt hinterlassen | Der übersetzte Inhalt eines Batches und seine Quittung committen in einer Transaktion. Die Quittung hält die Zähler, die Fehlerzeilen und die Abrechnungsdaten, und nie übersetzte Inhalte. Die Fortschrittsverbuchung leert atomar den Quittungsinhalt, erhöht die Auftragszähler und fügt eine Zeile in nice_translate_batch_completion ein |
| Anbieteraufruf höchstens einmal (BYOK) | Ein erneutes Einspielen ohne Quittung nach dem Anspruch wird zu einem dauerhaften Endfehler und gibt kein weiteres Geld aus | Bevor BYOK-Arbeit ihren nicht idempotenten Anbieter erreichen kann, hält das Manifest einen Versuchsanspruch fest. Stirbt ein Worker nach dem Anspruch, aber vor dem Anbieteraufruf, steht diese Arbeit nicht mehr automatisch zur Wiederholung bereit, denn die Alternative wäre eine zweite Zahlung. Managed-Aufrufe nutzen diesen Anspruch nicht. Ihre stabile Folge von Idempotenzschlüsseln hält das automatische Wiederholen auch nach einem unklaren Fehler sicher |
| Outbox für Abschluss-Events | Die Zustellung erfolgt mindestens einmal, und die Snapshots überleben den Auftrag | Die Zeile in nice_translate_finished_event entsteht in derselben Transaktion, die den Auftrag abschließt, und das Relay versendet erst nach diesem Commit. Ein fehlgeschlagener Versand behält seinen Lease-Zeitstempel, und die Wiederherstellung versucht ihn nach Ablauf des Stale-Fensters erneut. Die Snapshots sind unveränderlich und haben keinen Fremdschlüssel auf den Auftrag |
| Serialisierung doppelter Zustellungen | Zwei Zustellungen desselben Batches können nicht beide in den Anbieterpfad gelangen, und eine erneute Zustellung aktualisiert die Fehlerzeilen statt sie zu duplizieren | JobBatchExecutionLock nimmt eine verbindungsgebundene Advisory Lock (GET_LOCK/RELEASE_LOCK) auf die stabile Request-ID, und eine bereits abgeschlossene Request-ID läuft ins Leere. Die Fehlerzeilen verwenden deterministische IDs aus Request-ID und Positionsindex |
Achtung
Die Advisory Lock braucht eine feste, direkte MySQL- oder MariaDB-Sitzung für die Dauer eines Handler-Aufrufs. Datenbank-Proxys mit Transaction Pooling oder Multiplexing sind nicht unterstützt, sofern sie die Sitzungsbindung für GET_LOCK und RELEASE_LOCK nicht ausdrücklich erhalten.
Ausgewählte Grenzwerte für Ihre Kapazitätsplanung:
| Grenzwert | Wert |
|---|---|
| Gleichzeitig offene Batch-Nachrichten je Auftrag | 100 |
Batch-Größe (batchSize) | Standard 25, mindestens 5, höchstens 200 |
| Gespeicherte Fehlerzeilen je Auftrag | 500 |
| Wartezeit der Wiederherstellung vor der Übernahme nicht versendeter Arbeit | 60 s |
| Sicherheitsfenster für die erneute Zustellung unvollständiger Versandvorgänge | 900 s |
| Stale-Fenster für den Anspruch auf ein Abschluss-Event | 300 s |
| Aufbewahrung von Quittungen und Abschluss-Markern | 45 Tage |
| Verlaufs-IDs je Anfrage zum Zurücksetzen oder erneuten Anwenden | 200 |
Verzeichnisstruktur
| Pfad | Inhalt |
|---|---|
Api/ | Die Admin-API-Controller: Job, Provider, Katalog, Nutzung, Glossar, Verlauf |
Command/ | Die CLI-Befehle nice-translate:run und nice-translate:providers |
Core/Content/ | Die DAL-Definitionen: TranslationJob (und das Aggregat JobError), Glossary, Usage, TranslationHistory |
Engine/ | Die Übersetzungs-Pipeline, ihre DTOs und die Content-Walker |
Job/ | Die Auftragsorchestrierung: Konfiguration, Service, Batch-Store, Dispatcher und Lock, Queue-Nachrichten und Handler, geplante Aufgaben, Subscriber, Flow-Event |
Migration/ | Die Schema-Migrationen: Basisschema, Übersetzungsverlauf, Managed-Abrechnung, dauerhafte Batches, dauerhaftes Start-Relay, Outbox für Abschluss-Events, Batch-Absicherung und Aufräumarbeiten |
Provider/ | Die Anbieterverträge, die Registry, das Locale-Mapping, die abstrakten HTTP- und LLM-Basisklassen, die konkreten Anbieter |
Subscription/ | Die Anbindung von modernice All-in-One auf Basis von Shopware In-App Purchase |
Usage/ | Die Nutzungsaufzeichnung (UsageRecorder) |
Resources/config/ | services.xml, die Service-Dateien je Schicht und routes.xml |
Resources/app/administration/ | Das Vue-Administrationsmodul, seine Komponenten, Extensions und Snippets |
Erweiterungspunkte
| Erweiterungspunkt | Einstieg |
|---|---|
| Einen Übersetzungsanbieter ergänzen | TranslationProviderInterface implementieren und den Service mit nice_translate.provider taggen. Das optionale SupportsLiveModelsInterface ergänzt die Live-Modellliste. Siehe Eigene Anbieter |
| Auf abgeschlossene Aufträge reagieren | Der Flow-Builder-Trigger nice_translate.job.finished oder ein PHP-Subscriber. Siehe Events & Flow Builder |
| Die Erweiterung aus eigenem Code aufrufen | Die Routen unter /api/_action/nice-translate/. Siehe Admin-API |
| Auf den Berechtigungen der Erweiterung aufbauen | Die Rollen nice_translate.viewer und nice_translate.editor. Siehe Berechtigungen (ACL) |