Skip to content

Admin-API

Die Erweiterung registriert achtzehn Routen unter /api/_action/nice-translate/. Innerhalb der Erweiterung haben sie einen Konsumenten, das Administrationsmodul. Alles, was das Modul tut, erreichen Sie über dieselben Routen auch aus eigenen Werkzeugen.

Achtung

Behandeln Sie diese Routen als intern. Die Payloads und die Antwortstrukturen können sich zwischen Minor-Versionen ändern, ohne Deprecation-Zyklus, und für keine davon gilt eine Kompatibilitätszusage. Fixieren Sie die Version der Erweiterung, wenn Sie darauf aufbauen.

Authentifizierung

Jede Route deklariert den Route-Scope der Admin-API (PlatformRequest::ATTRIBUTE_ROUTE_SCOPE => [ApiRouteScope::ID]). Die Routen sind deshalb nur über die Admin-API von Shopware erreichbar, und nur mit einem gültigen Admin-API-Access-Token. Es gibt keine Store-API-Oberfläche und kein separates Authentifizierungsverfahren. Holen Sie sich ein Token über den Standard-OAuth-Endpunkt der Admin-API von Shopware (POST /api/oauth/token), und senden Sie es als Authorization: Bearer <token>.

Berechtigungen

Jede Route deklariert die benötigte Berechtigung über PlatformRequest::ATTRIBUTE_ACL. Fehlt der Integration oder dem Benutzer eines Tokens diese Berechtigung, weist Shopware die Anfrage ab, bevor der Controller läuft. Berechtigungen (ACL) beschreibt die beiden Rollen.

Die Berechtigung folgt nicht immer der HTTP-Methode, schlagen Sie sie deshalb je Route nach. POST /estimate berechnet lediglich eine Schätzung und braucht trotzdem nice_translate.editor. GET|POST /provider/{providerId}/models braucht für beide Methoden nur nice_translate.viewer. Aus diesem Grund hat jede Routentabelle auf dieser Seite eine eigene Spalte für die Berechtigung.

BerechtigungRouten
nice_translate.viewerGET /entities, GET /coverage, GET /providers, GET|POST /provider/{providerId}/models, GET /usage, GET /subscription, GET /glossary/export
nice_translate.editorPOST /estimate, POST /job, POST /job/{jobId}/cancel, POST /job/{jobId}/retry, POST /job/{jobId}/revert, POST /provider/{providerId}/test, POST /preview, POST /subscription/refresh, POST /glossary/import, POST /history/revert, POST /history/reapply

Ein vollständiges Beispiel

Das Beispiel schätzt einen Lauf, bevor es den Auftrag anlegt. Es nutzt dafür dieselben zwei Aufrufe wie der Assistent:

bash
curl -sS -X POST "https://shop.example/api/_action/nice-translate/estimate" -H "Authorization: Bearer $SW_TOKEN" -H "Content-Type: application/json" -d '{"config":{"entities":[{"name":"product","scope":"missing"}],"targetLanguageIds":["a1b2c3d4e5f60718293a4b5c6d7e8f90"],"providerId":"deepl","options":{"mode":"missing"}}}'
json
{
  "items": 128,
  "characters": 41230,
  "exact": true,
  "cost": { "amount": 1.0308, "currency": "USD" },
  "credits": null,
  "serviceTier": null,
  "multiplier": null,
  "quota": null,
  "warnings": []
}

Wenn die Schätzung passt, legen Sie den Auftrag mit demselben config-Objekt an:

bash
curl -sS -X POST "https://shop.example/api/_action/nice-translate/job" -H "Authorization: Bearer $SW_TOKEN" -H "Content-Type: application/json" -d '{"config":{"entities":[{"name":"product","scope":"missing"}],"targetLanguageIds":["a1b2c3d4e5f60718293a4b5c6d7e8f90"],"providerId":"deepl","options":{"mode":"missing"}}}'
json
{ "jobId": "018f2c1a8f7c73f2b0a4a1b7d1e5c904" }

Ihre Queue-Worker verarbeiten den Auftrag danach. Seinen Status fragen Sie über die DAL-Entität nice_translate_job ab (siehe Entitäten über die DAL-API).

Das JobConfig-Objekt

POST /estimate und POST /job erwarten beide ein einzelnes config-Objekt, und JobConfig::fromArray() normalisiert es. Unbekannte Schlüssel bleiben unverändert. Den ungültigen Wert ersetzt der Normalisierer durch den dokumentierten Standard.

json
{
  "entities": [{ "name": "product", "scope": "all|missing|selection", "ids": null }],
  "sourceLanguageId": null,
  "targetLanguageIds": ["<uuid>"],
  "providerId": "deepl",
  "options": {
    "mode": "missing",
    "protectManualEdits": true,
    "skipUnchanged": true,
    "fields": null,
    "includeCustomFields": true,
    "tone": "default",
    "customPrompt": null,
    "glossary": true,
    "seoTruncate": true,
    "serviceTier": "balanced"
  }
}
FeldNormalisierung
entities[].nameEine reine Zeichenkette als Entität wird zu {"name": …}
entities[].scopeall, missing oder selection. Ein ungültiger Wert wird zu selection, wenn ids vorhanden sind, und sonst zu all
entities[].idsDer Normalisierer verwirft die Nicht-String-Einträge und die leeren Einträge und dedupliziert die Liste. Eine leere Liste wird zu null. Bei scope: "selection" muss jeder Eintrag eine gültige UUID sein
sourceLanguageIdEine leere Zeichenkette wird zu null, und die Erweiterung löst die Quelle dann je Zielsprache auf
targetLanguageIdsDer Normalisierer verwirft die leeren Einträge und dedupliziert die Liste
options.modeall, andernfalls missing
options.tonedefault, formal oder informal. Jeder andere Wert wird zu default
options.serviceTierspeed, balanced oder quality. Jeder andere Wert wird zu balanced
options.customPromptWird getrimmt. Ein leerer Wert wird zu null
options.fields{"<entityName>": ["<property>", …]}. Ein leeres Ergebnis wird zu null
options.protectManualEdits, skipUnchanged, includeCustomFields, glossary, seoTruncateWerden als Boolean geparst

serviceTier erreicht den Anbieter nur auf dem Managed-Weg. Übersetzungsassistent und Schutzmechanismen beschreiben die Wirkung der übrigen Optionen auf den Lauf.

Aufträge

MethodePfadBerechtigung
POST/api/_action/nice-translate/estimatenice_translate.editor
POST/api/_action/nice-translate/jobnice_translate.editor
POST/api/_action/nice-translate/job/{jobId}/cancelnice_translate.editor
POST/api/_action/nice-translate/job/{jobId}/retrynice_translate.editor
POST/api/_action/nice-translate/job/{jobId}/revertnice_translate.editor

POST /estimate

Anfrage: {"config": <JobConfig>}. Felder der Antwort:

FeldTypBedeutung
itemsintDatensätze, die verarbeitet würden
charactersintZeichen, die gesendet würden
exactboolfalse, sobald mindestens eine Entität nur näherungsweise bestimmt werden konnte
cost{amount, currency}Schätzung des Anbieters, auf vier Nachkommastellen gerundet
creditsint | nullNur beim Anbieter managed gesetzt
serviceTierstring | nullNur beim Anbieter managed gesetzt
multiplierint | nullGuthaben-Multiplikator der aufgelösten Stufe
quotaobject | nullMomentaufnahme des Managed-Kontingents
warningslistEinträge der Form {code, params, message}

Die Schätzung ist unverbindlich. Der Platzhalterschutz und die Prüfungen des Zielzustands laufen später und können den gesendeten Text und die abgerechnete Menge verändern. Ein Anbieter, dessen eigene Schätzung eine Exception wirft, steuert 0.0 USD bei und lässt den Aufruf nicht scheitern.

Die Route liefert genau diese elf Warncodes: unknown_provider, provider_not_configured, target_language_missing, source_equals_target, locale_unmappable, google_english_collapse, entity_unsupported, estimate_failed, snippet_source_set_missing, snippet_target_set_missing, managed_quota_exceeded. Kosten & Verbrauch listet den Meldungstext jedes Codes und seine Bedeutung für den Händler.

Fehler: 400 {"message": "Missing \"config\" object in the request body."} oder 400 mit der Validierungsmeldung, wenn die Konfiguration ungültig ist.

POST /job

Anfrage: {"config": <JobConfig>}. Antwort: {"jobId": "<hex uuid>"}.

Fehler: 400 bei fehlendem config, und sonst 400 mit der Validierungsmeldung.

Hinweis

Wenn der Versand in die Queue fehlschlägt, liefert der Aufruf dennoch 200 mit der Auftrags-ID zurück. Die committete queued-Zeile ist die dauerhafte Startmarkierung, und die geplante Wiederherstellung stellt die Nachricht nachträglich zu. Siehe Architektur.

POST /job/{jobId}/cancel

Kein Body. Der Controller wandelt {jobId} in Kleinbuchstaben um und validiert die ID als UUID. Antwort: {"status": "cancelled"}, also der Status, den der Controller nach dem Versuch erneut gelesen hat. Nur ein queued-Auftrag und ein running-Auftrag wechseln den Status.

Fehler: 400 {"message": "Invalid job id."}, 404 {"message": "Translation job not found."}.

POST /job/{jobId}/retry

Antwort: {"retried": <int>}. Die Route erwartet keinen Body.

Nur ein completed-Auftrag und ein completed_with_errors-Auftrag können wiederholen. Jeder andere Auftrag liefert 400 {"message": "Only completed jobs can retry failed items."}. Der erneute Lauf klammert die Fehlerzeilen der teilweise übersetzten Datensätze aus. Bereits geschriebene Inhalte bleiben unverändert.

POST /job/{jobId}/revert

Kein Body. Antwort: {"queued": true, "items": <int>}. Das Zurücknehmen versendet zusätzlich eine RevertJobMessage.

Fehler: 400 bei ungültiger ID; 404 bei unbekanntem Auftrag; 400 {"message": "The job is still queued or running and cannot be reverted yet."}; 400 {"message": "The job has no revertible history rows."}, wenn der Auftrag keine Verlaufszeile im Status applied hat.

Anbieter

MethodePfadBerechtigung
GET/api/_action/nice-translate/providersnice_translate.viewer
GET, POST/api/_action/nice-translate/provider/{providerId}/modelsnice_translate.viewer
POST/api/_action/nice-translate/provider/{providerId}/testnice_translate.editor
POST/api/_action/nice-translate/previewnice_translate.editor

GET /providers

Keine Parameter. Die Route liefert jeden Service mit dem Tag nice_translate.provider, auch Ihren eigenen Service:

json
{
  "providers": [
    {
      "id": "deepl",
      "label": "DeepL",
      "configured": true,
      "models": [],
      "supports": { "html": true, "formality": true, "tone": false, "glossary": false },
      "viaSubscription": false
    }
  ]
}

models ist die kuratierte Liste des Anbieters, und sie ist bei einem reinen maschinellen Übersetzer leer. Jeder Eintrag hat id, name, tier, pricing und source. viaSubscription ist genau dann true, wenn die Anbieter-ID managed ist.

GET | POST /provider/{providerId}/models

Optionaler POST-Body: {"apiKey": "…"}, für einen Schlüssel, den jemand in der Administration eingegeben, aber nicht gespeichert hat. Einen leeren Wert und einen Wert aus reinen Leerzeichen normalisiert der Controller zu null.

Wenn der Anbieter SupportsLiveModelsInterface implementiert und die Anfrage einen Schlüssel übergeben hat oder der Anbieter bereits konfiguriert ist, versucht der Controller eine Live-Abfrage. Jeder Fehler fällt still auf die kuratierte Liste zurück.

Antwort: {"models": [...], "source": "live"} oder {"models": [...], "source": "curated"}.

Fehler: 404 {"message": "Unknown translation provider \"x\"."}.

POST /provider/{providerId}/test

Body: {"apiKey": "…"} (optional; ohne Angabe nutzt der Controller den gespeicherten Schlüssel). Die Antwort ist immer 200:

json
{ "valid": true, "message": "API key is valid.", "quota": { "used": 0, "limit": null } }

quota ist null, wenn der Anbieter kein Kontingent meldet, und quota.limit ist null bei einem unbegrenzten Tarif. Der Controller überführt eine ProviderException in {"valid": false, "message": "<user-safe message>", "quota": null}. Er gibt den Schlüssel nie zurück.

Fehler: 404 bei unbekanntem Anbieter.

POST /preview

Die Route übersetzt einen einzelnen Beispieltext. Sie baut die Anfrage immer mit format: "text" und ohne Glossarbegriffe auf. Keine Oberfläche der Administration ruft diese Route auf. Sie existiert für API-Clients.

json
{
  "providerId": "openai",
  "text": "Sample sentence.",
  "sourceLanguageId": "<uuid>",
  "targetLanguageId": "<uuid>",
  "tone": "formal",
  "customPrompt": null
}

providerId, ein nicht leerer text, sourceLanguageId und targetLanguageId sind Pflicht. tone akzeptiert der Controller nur als formal oder informal, und jeden anderen Wert behandelt er als nicht gesetzt.

Antwort: {"translation": "…", "characters": <int>, "cost": {"amount": <float>, "currency": "USD"}}.

Fehler: 400 {"message": "The fields \"providerId\", \"text\", \"sourceLanguageId\" and \"targetLanguageId\" are required."}; 400 {"message": "Invalid language id."}; 400 {"message": "The source or target language could not be resolved to a locale."}; 400 mit der anwenderfreundlichen Meldung des Anbieters, wenn dieser eine Exception wirft; 404 bei unbekanntem Anbieter.

Katalog

MethodePfadBerechtigung
GET/api/_action/nice-translate/entitiesnice_translate.viewer
GET/api/_action/nice-translate/coveragenice_translate.viewer

GET /entities

Keine Parameter. Die Route listet die übersetzbaren Inhaltstypen mit ihren Bezeichnungen, der Gesamtzahl der Datensätze und den aufgelösten Feld-Specs:

json
{
  "entities": [
    {
      "name": "product",
      "label": "Products",
      "total": 1234,
      "fields": [{ "property": "name", "type": "text", "maxLength": 255 }]
    }
  ]
}

fields[].type ist text, html, custom_fields, string_list oder structure. Die Entitäten, die der Installation fehlen, lässt die Route weg. Die Liste endet immer mit der Pseudo-Entität snippet. Unterstützte Inhalte beschreibt, welche Inhaltstypen erscheinen und warum.

GET /coverage

Query-ParameterBedeutungStandard, wenn nicht angegeben
languageIdsKommagetrennte Sprach-UUIDsAlle Sprachen außer der Systemsprache, nach Namen sortiert
entitiesKommagetrennte Entitätsnamenproduct,category,cms_page,snippet
json
{
  "rows": [
    { "entity": "product", "languageId": "<hex>", "total": 100, "translated": 40, "percent": 40.0 }
  ]
}

Fehler: 400 {"message": "Invalid language id \"…\"."}; 400 {"message": "Unsupported entities: ….."}.

Die Abdeckung zählt die Datensätze mit einer eigenen Übersetzung der primären Textspalte ihrer Entität. Damit eignet sie sich, um Abweichungen zu finden. Die Auflösung endet beim Datensatz: Ein Datensatz gilt auch dann als übersetzt, wenn einzelne seiner Felder noch leer sind. Siehe Abdeckungsbericht.

Verbrauch und Abonnement

MethodePfadBerechtigung
GET/api/_action/nice-translate/usagenice_translate.viewer
GET/api/_action/nice-translate/subscriptionnice_translate.viewer
POST/api/_action/nice-translate/subscription/refreshnice_translate.editor

GET /usage

Query-Parameter months (numerisch, begrenzt auf 1 bis 36, Standard 6). Die Route liefert den neuesten Zeitraum zuerst.

json
{
  "months": [
    {
      "period": "2026-07",
      "providers": {
        "deepl": {
          "characters": 41230,
          "inputTokens": 0,
          "outputTokens": 0,
          "requests": 12,
          "cost": { "amount": 1.0308, "currency": "USD" }
        }
      }
    }
  ]
}

Diese Beträge sind die eigenen Aufzeichnungen der Erweiterung. Maßgeblich bleibt die Rechnung Ihres Anbieters. Siehe Kosten & Verbrauch.

GET /subscription

Keine Parameter.

json
{
  "available": false,
  "active": true,
  "state": "active",
  "plan": "scale",
  "identifier": "NiceTranslateAllInOneScale",
  "plans": [
    { "plan": "enterprise", "identifier": "NiceTranslateAllInOneEnterprise", "credits": 96000000 },
    { "plan": "scale", "identifier": "NiceTranslateAllInOneScale", "credits": 32000000 },
    { "plan": "growth", "identifier": "NiceTranslateAllInOneGrowth", "credits": 12000000 },
    { "plan": "starter", "identifier": "NiceTranslateAllInOneStarter", "credits": 4000000 }
  ],
  "quota": null,
  "reachable": true
}

state ist active, available, expired oder disconnected. available ist genau dann true, wenn state === "available" gilt. modernice All-in-One beschreibt die Bedeutung der Zustände für einen Händler.

POST /subscription/refresh

Kein Body. Die Route stößt den Updater für In-App-Käufe von Shopware an und leitet danach dieselbe Statusstruktur ab wie GET /subscription. Die Administration fragt diese Route nach einem Kauf ab. Ohne sie würde ein neuer Kauf erst mit der täglichen geplanten Aktualisierung erscheinen.

Fehler:

json
{
  "errors": [
    {
      "code": "MO_TRANSLATE__STORE_DISCONNECTED",
      "detail": "Connect this administration user to the Shopware Store before refreshing purchases."
    }
  ]
}

Die Route liefert das mit 412, wenn der Administrationsbenutzer nicht am Shopware Store angemeldet ist, und

json
{
  "errors": [
    {
      "code": "MO_TRANSLATE__SUBSCRIPTION_REFRESH_FAILED",
      "detail": "The Shopware in-app purchase state could not be refreshed."
    }
  ]
}

mit 502, wenn der Updater eine Exception wirft.

Glossar

MethodePfadBerechtigung
POST/api/_action/nice-translate/glossary/importnice_translate.editor
GET/api/_action/nice-translate/glossary/exportnice_translate.viewer

POST /glossary/import

Body: {"csv": "…", "delimiter": ";"}. delimiter ist optional und muss genau ein Zeichen lang sein. Ein UTF-8-BOM am Anfang von csv entfernt der Controller.

Die Kopfzeile muss mit term, mode und caseSensitive beginnen (Vergleich ohne Beachtung der Groß-/Kleinschreibung), danach folgt eine Spalte je Locale-Code:

csv
term;mode;caseSensitive;de-DE;fr-FR
modernice;keep;1;;
Delivery time;replace;0;Lieferzeit;Délai de livraison

Behandlung der Zeilen: Einen leeren term überspringt der Controller. Ein leerer mode wird zu keep. Ein mode außerhalb von keep und replace überspringt die Zeile. caseSensitive ist true bei 1, true oder yes. Der Controller dedupliziert die Zeilen über den kleingeschriebenen Begriff, aktualisiert die vorhandenen Begriffe und legt die neuen Begriffe als aktiv an.

Antwort: {"imported": <int>, "created": <int>, "updated": <int>, "skipped": <int>}.

Fehler: 400 {"message": "Missing \"csv\" content in the request body."}; 400 {"message": "The delimiter must be a single character."}; 400 {"message": "The CSV content is empty."}; 400 {"message": "Invalid CSV header. Expected columns: term;mode;caseSensitive;<localeCode>;..."}; 400 {"message": "Unknown locale code(s) in CSV header: ….."}.

GET /glossary/export

Keine Parameter. Die Route liefert text/csv; charset=utf-8 mit UTF-8-BOM und Content-Disposition: attachment; filename=nice-translate-glossary.csv. Das Trennzeichen ist ;. Die Spalten sind term;mode;caseSensitive, danach folgt eine Spalte je vorkommendem Locale-Code, sortiert ohne Beachtung der Groß-/Kleinschreibung. caseSensitive gibt die Route als 1 oder 0 aus. Der Export enthält immer das vollständige Glossar, und Sie importieren ihn erneut, um die passenden Begriffe zu aktualisieren.

Verlauf

MethodePfadBerechtigung
POST/api/_action/nice-translate/history/revertnice_translate.editor
POST/api/_action/nice-translate/history/reapplynice_translate.editor

Beide Routen erwarten {"ids": ["<uuid>", …]} und liefern dieselbe Struktur:

json
{ "done": 3, "skipped": [{ "id": "<uuid>", "reason": "…" }] }

Beide Richtungen vergleichen vor dem Schreiben den aktuellen direkten Zielwert mit dem erwarteten Wert aus dem Verlauf. Einen Datensatz, den jemand zwischenzeitlich geändert hat oder der im falschen Status ist, melden die Routen in skipped und lassen ihn unverändert. Siehe Übersetzungsverlauf.

Fehler: 400 {"message": "Missing \"ids\" array in the request body."}; 400 {"message": "A maximum of 200 ids can be processed per request."}.

Entitäten über die DAL-API

Die Auftragsliste und das Auftragsdetail, die Auftragsfehler, das Glossar-CRUD, die Nutzungszeilen und die Verlaufszeilen haben keine eigenen Routen. Sie sind registrierte DAL-Entitäten und nutzen die Repository-Endpunkte, die Shopware erzeugt:

EntitätLeseberechtigungSchreibberechtigungen
nice_translate_jobnice_translate_job:readnice_translate_job:create, :update, :delete
nice_translate_job_errornice_translate_job_error:read
nice_translate_glossarynice_translate_glossary:readnice_translate_glossary:create, :update, :delete
nice_translate_usagenice_translate_usage:read
nice_translate_historynice_translate_history:readnice_translate_history:update

Jedes Feld dieser Entitäten ist als ApiAware(AdminApiSource::class) deklariert. Die Entitäten sind deshalb nur über die Admin-API lesbar, und in der Store-API erscheinen sie nie. Die übrigen sechs Tabellen der Erweiterung sind reine DBAL-Aufzeichnungen ohne Definition, ohne Repository und ohne API-Ressource. Siehe Architektur.

modernice Extensions für Shopware 6. Shopware ist eine Marke der shopware AG — diese Dokumentation steht in keiner Verbindung zur shopware AG.