cyllene-digital / sylius-ai-translation-plugin
AI-assisted translation management for the Sylius 2 admin: browse every translation catalogue, override any wording per channel without touching the files, and review AI suggestions (OpenAI-compatible, Anthropic, DeepL) before they go live.
Package info
github.com/CylleneDigital/SyliusAiTranslationPlugin
Type:sylius-plugin
pkg:composer/cyllene-digital/sylius-ai-translation-plugin
Requires
- php: ^8.3
- ext-intl: *
- cyllene-digital/ai-translation-bundle: ^1.0
- doctrine/persistence: ^3.1 || ^4.0
- pagerfanta/core: ^3.7 || ^4.0
- psr/cache: ^3.0
- psr/clock: ^1.0
- sylius/sylius: ^2.1
- symfony/cache: ^7.4 || ^8.0
- symfony/clock: ^7.4 || ^8.0
- symfony/config: ^7.4 || ^8.0
- symfony/dependency-injection: ^7.4 || ^8.0
- symfony/doctrine-bridge: ^7.4 || ^8.0
- symfony/form: ^7.4 || ^8.0
- symfony/framework-bundle: ^7.4 || ^8.0
- symfony/http-foundation: ^7.4 || ^8.0
- symfony/http-kernel: ^7.4 || ^8.0
- symfony/messenger: ^7.4 || ^8.0
- symfony/routing: ^7.4 || ^8.0
- symfony/security-bundle: ^7.4 || ^8.0
- symfony/security-csrf: ^7.4 || ^8.0
- symfony/serializer: ^7.4 || ^8.0
- symfony/service-contracts: ^3.5
- symfony/translation-contracts: ^3.5
- symfony/validator: ^7.4 || ^8.0
- symfony/yaml: ^7.4 || ^8.0
- twig/twig: ^3.10
Requires (Dev)
- behat/behat: ^3.22 || ^4.0
- behat/mink: ^1.11
- behat/mink-browserkit-driver: ^2.2
- dbrekelmans/bdi: ^1.4
- dmore/chrome-mink-driver: ^2.9
- friends-of-behat/mink-debug-extension: ^2.1
- friends-of-behat/mink-extension: ^2.7 || ^3.0@alpha
- friends-of-behat/page-object-extension: ^0.4
- friends-of-behat/suite-settings-extension: ^1.1
- friends-of-behat/symfony-extension: ^2.6.2
- friends-of-behat/variadic-extension: ^1.7
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.2
- phpstan/phpstan-doctrine: ^2.0
- phpstan/phpstan-webmozart-assert: ^2.0
- phpunit/phpunit: ^12.2
- sylius-labs/behat-chrome-extension: ^1.5
- sylius-labs/coding-standard: ^4.4
- sylius-labs/suite-tags-extension: ~0.2
- sylius/test-application: ~2.1.0@alpha || ~2.2.0@alpha || ~2.3.0@alpha
- symfony/browser-kit: ^7.4 || ^8.0
- symfony/css-selector: ^7.4 || ^8.0
- symfony/debug-bundle: ^7.4 || ^8.0
- symfony/dotenv: ^7.4 || ^8.0
- symfony/finder: ^7.4 || ^8.0
- symfony/intl: ^7.4 || ^8.0
- symfony/runtime: ^7.4 || ^8.0
- symfony/web-profiler-bundle: ^7.4 || ^8.0
- symfony/webpack-encore-bundle: ^2.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-13 18:05:29 UTC
README
Sylius AI Translation Plugin
Back-office integration of the AI translation engine for Sylius 2: browse the project's translation catalogues, override any wording at runtime (per sales channel if needed), and let AI providers suggest the missing translations, with a per-channel context for the AI, under human review, always.
Compatibility
| Component | Versions |
|---|---|
| PHP | ^8.3 (Symfony 8: ^8.4) |
| Sylius | 2.1, 2.2, 2.3 |
| Symfony | ^7.4, or ^8.0 with Sylius 2.3 |
cyllene-digital/ai-translation-bundle |
^1.0 |
In a Symfony 7.4 project, pin the stack with extra.symfony.require: "7.4.*" (standard Flex
practice): without it Composer may mix Symfony 8 components (symfony/var-exporter…) into a
7.4 framework, which breaks Doctrine proxy generation at boot.
What this plugin does
Two admin screens plugged into the standard Sylius layout (menu, twig hooks, admin firewall):
- Catalogue: browse the host project's
translations/by Category / Domain / Type, per-locale coverage cards linking straight to generation, edition of any value in a modal (the override is stored in database), per-channel overrides with global fallback, cross-catalogue search and "modified only" view, view-snapshot export/import in XLIFF or CSV (originals included, re-import only keeps the changes), one-click purge of orphan overrides; - AI suggestions: per-catalogue generation with a volume/cost estimate before spending anything, review of pending suggestions (single or bulk approve/reject, the value editable before approval, confidence/provider/placeholder badges), a failed generation staying in the list, flagged, so it can be retried or written by hand, and the last runs of the channel, failures included;
- Themes: every
themes/*/translationsdirectory is auto-discovered and browsed as@ThemeName/…catalogues, with the same precedence as the theme files at runtime; - Sylius adapters: locales come from Configuration → Locales, and the logged-in admin signs every override and every review decision.
flowchart TB
subgraph editorial["Editorial time: back-office & CLI"]
direction LR
F["translations/ files<br/>(yaml · xliff · json)"] -- "scan<br/>Category / Domain / Type" --> C["Catalogue screen<br/>(browse, search, coverage)"]
C -- "edit a value" --> O
C -- "generate missing keys" --> AI["AI providers<br/>default · openai · anthropic · deepl · custom"]
AI --> S["Pending suggestions"]
S -- "approve (human review, audited)" --> O["Overrides<br/>(database · optional channel scope)"]
S -. "reject" .-> X["Deleted<br/>(the key can be generated again)"]
end
subgraph runtime["Runtime: every trans() call"]
direction LR
T["Decorated translator"] --> R1{"channel<br/>override?"} -- "no" --> R2{"global<br/>override?"} -- "no" --> R3["file value"]
end
O -. "read-through cache" .-> T
Loading
Two properties matter more than the rest:
No file is ever written. Overrides are database deltas applied over the compiled
catalogues: they survive redeployments and immutable containers, the original value is always
one click away, and removing an override restores the inherited or file value instantly. A
regional locale inherits its language's overrides too (fr_FR falls back on fr), and the
screens say where an inherited value comes from.
Nothing AI-generated is applied without a human. Generation produces pending suggestions;
only an explicit approval turns one into an override, kept with who approved it and when; a
rejected suggestion is deleted. A placeholder guard blocks approvals that would break ICU/%name% parameters.
What this plugin does not do
- It is not the engine. Providers, overrides, caches, console commands and the configuration
tree live in
cyllene-digital/ai-translation-bundle, which also works without Sylius. - It does not translate entity content. Product names and descriptions are Sylius translation data with its own tooling: the scope stops at the project's message catalogues.
- It never writes a translation file, and applies nothing an AI generated without an explicit approval.
- It has no API and no shop screen: both screens are in the back office, and the shop only shows the result.
Installation
composer config extra.symfony.allow-contrib true composer require cyllene-digital/sylius-ai-translation-plugin # installs the engine too
With Symfony Flex, the recipes do steps 1 and 2 and copy a commented
config/packages/cyllene_digital_ai_translation.yaml: go to step 3. Without Flex, follow every
step.
- Register the engine, then the plugin after it:
// config/bundles.php CylleneDigital\AiTranslationBundle\CylleneDigitalAiTranslationBundle::class => ['all' => true], CylleneDigital\SyliusAiTranslationPlugin\CylleneDigitalSyliusAiTranslationPlugin::class => ['all' => true],
- Import the admin routes:
# config/routes/cyllene_digital_sylius_ai_translation.yaml cyllene_digital_sylius_ai_translation: resource: '@CylleneDigitalSyliusAiTranslationPlugin/config/routes.yaml'
-
Configure the engine under its
cyllene_digital_ai_translationkey (providers,translations_path,default_locale,llm_context…) - in the file the recipe copied, or one you create without Flex. -
Version the engine tables in your migrations: the engine ships its Doctrine mapping, not migrations (re-run the diff after updating it, too):
bin/console doctrine:migrations:diff && bin/console doctrine:migrations:migrate
- Import the plugin's admin entrypoint from yours, then build your assets. It carries the stylesheet and the Stimulus controllers of both screens: without it they render unstyled and the modals, the cost estimate and the channel selector stay inert.
// assets/admin/entrypoint.js import '../../vendor/cyllene-digital/sylius-ai-translation-plugin/assets/admin/entrypoint.js';
yarn install yarn build
- Grant the roles the screens require: without one they answer 403 and the menu entry is not shown. Everything to every admin, or one role per kind of action (permissions):
# config/packages/security.yaml security: role_hierarchy: ROLE_ADMINISTRATION_ACCESS: [ROLE_AI_TRANSLATION_MANAGE]
Every step in detail, with and without Flex: installation.
In production
Generation runs asynchronously on the engine's own Messenger transport: keep a worker on
bin/console messenger:consume cyllene_ai_translation. The plugin dispatches the runs on its
own bus (cyllene_digital_ai_translation.bus), not on sylius.command_bus: that one wraps
handlers in doctrine_transaction, and a failing chunk would roll back the suggestions already
billed. Dispatch GenerateSuggestionsMessage yourself? Use that bus too.
An uploaded import waits for its confirmation, one hour at most, in the
cyllene_digital_sylius_ai_translation.import_staging cache pool, built on cache.app. Behind a load
balancer, the confirmation only finds the upload if that cache is shared between the nodes
(Redis…), or point the pool itself at a shared adapter:
# config/packages/cache.yaml framework: cache: pools: cyllene_digital_sylius_ai_translation.import_staging: adapter: cache.adapter.redis
Troubleshooting
| Symptom | Cause |
|---|---|
| Both screens render unstyled, the modals and the cost estimate do nothing | The admin entrypoint is not imported, or the assets were not rebuilt (assets) |
| The screens answer 403, or the Translations menu entry is missing | No role of the plugin is granted (permissions) |
| A generation is queued and no suggestion arrives | No worker consumes cyllene_ai_translation; a run that failed shows in the journal under the list |
Cannot reorder children, order does not contain all children. |
The project reorders its admin menu with a fixed list (menu) |
| An import file is refused for its size although it is under 8 MB | PHP accepts less (upload_max_filesize, post_max_size): the import window announces the limit that really applies |
| An import is refused as expired right after the upload | The upload and its confirmation were served by two nodes that do not share cache.app (see In production) |
Upgrading
Every compatibility break is written in UPGRADE.md before it is released, with the steps to carry out. The engine has its own upgrade guide.
Public contract
The bundle class, the admin routes, the Twig hooks and templates, the grids, the roles, the menu entry and the generation bus only change in a major version. The full list, what is internal and the support policy: public contract.
Documentation
- Back-office guide: both screens in detail, for administrators
- Theming and Twig hooks: templates, Twig hooks, grids, the menu entry, for integrators
- Technical documentation: every page, by audience
- Engine documentation: concepts, provider bridges, configuration reference, CLI, caches
Status
Exercised on a real Sylius 2.3 back office: the functional tests boot the plugin inside the Sylius test application against a real database, log an admin in and drive both screens through the HTTP kernel, the suggestion workflow included.
The continuous integration matrix runs the unit, integration and functional tests, 45 Behat scenarios
(13 of them in headless Chrome, on the primary combination; 12 reading the result in the shop), PHPStan at max level, the coding
standard, container, template and translation linting and a strict composer validate on PHP
8.3, 8.4 and 8.5, against Sylius 2.1, 2.2 and 2.3 on MySQL 8.4 - and, on the primary
combination, on MySQL 8.0, MariaDB 10.11 and 11.4, PostgreSQL 15, 16 and 17.
Both screens are driven by a channel filter: coverage, counters, browsing, suggestion generation and review all happen for the selected channel (or globally).
Contributing
CONTRIBUTING.md - setting up the test environment, standards, what has to pass.
A flaw is reported privately: SECURITY.md. Do not open it as a public issue.
Provenance and licence
Released under the MIT licence: see LICENSE.
Package: cyllene-digital/sylius-ai-translation-plugin
Maintained by Cyllene, on GitHub as @CylleneDigital

