Translator¶
The Translator brings AI-powered translation to several places in the Sulu admin: individual text fields, full documents when copying a locale, categories, and media metadata.
Text translation¶
Editors can translate the content of text fields directly in the admin. The action is gated by the sulu.ai.translate security context (category Sulu AI).
Full content translation¶
When an editor copies content from another locale and chooses the translate option, the bundle translates the whole document in one pass instead of copying it verbatim.
Out of the box this works for:
- Pages
- Articles (when
SuluArticleBundleis installed) - Snippets
on both Sulu 2.6 and Sulu 3.0.
What gets translated¶
The bundle walks the document's form metadata and translates:
- All properties whose types are configured as plain-text field types (default:
text_line,text_area) - All properties whose types are configured as HTML field types (default:
text_editor) — translated with HTML handling so markup survives - Block content, including nested and global blocks, and
image_maphotspot content - SEO extension data (title, description, keywords) and excerpt extension data (title, description, more)
- Selected sub-properties of complex property types via
property_type_translation_properties(default: thetitleoflinkproperties and thetitle/descriptionofteaser_selectionitems)
If your project uses custom property types that contain translatable text, add them to the configuration:
sulu_ai_platform:
text_field_types: ['text_line', 'text_area', 'my_custom_text_type']
html_field_types: ['text_editor', 'my_custom_editor_type']
property_type_translation_properties:
my_complex_type: ['items/*/label']
URL generation¶
After translating, the bundle regenerates the resource locator for the new locale from the translated content — using the fields tagged with sulu.rlp.part (falling back to the title) and respecting a route_schema option on the URL field. Homepage documents are excluded.
Enabled languages¶
The set of target languages offered to editors is a fixed list of DeepL-supported languages. Individual languages can be disabled:
sulu_ai_platform:
translator:
languages:
BG: false # hide Bulgarian as a target language
Note: The
locale_mapdescriptionfield is not used for translations — it only provides language context for the generator experts.
Category translation¶
Category names and metadata can be translated into other locales from the category management in the admin.
Media metadata translation¶
Media titles and descriptions (alt text and caption) can be translated into the media's other locales — see Media Metadata Generator.
Translating custom entities¶
Full content translation is not available out of the box for custom entities (for example entities built on the content bundle). The supported path is to mirror what the bundle does for snippets — the snippet integration is deliberately the simplest reference implementation.
The pattern is the same on Sulu 2.6 and Sulu 3.0; the example below shows Sulu 2.6, on Sulu 3.0 use Sulu3FullContentTranslationSubscriber::onSnippetTranslationCopied() as the reference instead:
- Extend
Sulu\Bundle\AiPlatformBundle\Features\FullContentTranslation\AbstractFullContentTranslationSubscriber. - Subscribe to the event your entity dispatches when a translation is copied (the equivalent of
SnippetTranslationCopiedEvent). - Wrap the document and call
handleContentTranslation()— exactly likeSulu26FullContentTranslationSubscriber::onSnippetTranslationCopied()does:
<?php
namespace App\EventSubscriber;
use App\Domain\Event\CustomEntityTranslationCopiedEvent;
use Sulu\Bundle\AiPlatformBundle\Compatibility\ContentPersister\Sulu26ContentDecorator;
use Sulu\Bundle\AiPlatformBundle\Features\FullContentTranslation\AbstractFullContentTranslationSubscriber;
final class CustomEntityFullContentTranslationSubscriber extends AbstractFullContentTranslationSubscriber
{
public static function getSubscribedEvents(): iterable
{
yield CustomEntityTranslationCopiedEvent::class => 'onTranslationCopied';
}
public function onTranslationCopied(CustomEntityTranslationCopiedEvent $event): void
{
$document = $event->getDocument();
$this->handleContentTranslation(
'custom_entity_key', // form metadata key of your entity
new Sulu26ContentDecorator($document), // adapter around your document
$event->getResourceLocale(),
$event->getEventContext()['sourceLocale'] ?? null,
$event->getEventPayload() ?? [],
$document->getStructureType(),
);
}
protected function generateUrl(array $parts, string $parentPath, string $urlFieldType, string $locale, ?string $routeSchema): string
{
// Only needed when your entity has a route/resource_locator field;
// see Sulu26FullContentTranslationSubscriber for a route-generator-based implementation.
return $parentPath . '/' . \implode('-', $parts);
}
}
- Register the subscriber with the same dependencies as the built-in one:
// config/services.php
$services->set(CustomEntityFullContentTranslationSubscriber::class)
->args([
service('sulu_ai_platform.translator'),
service('sulu_admin.structure_form_metadata_loader'),
service('sulu_admin.metadata_provider_registry'),
service(ContentRepositoryInterface::class),
service('request_stack'),
service(SecurityCheckerInterface::class),
param('sulu_ai_platform.property_type_translation_properties'),
param('sulu_ai_platform.text_field_types'),
param('sulu_ai_platform.html_field_types'),
])
->tag('kernel.event_subscriber');
Notes:
Sulu26ContentDecoratorwraps PHPCR-based documents. If your custom entity is Doctrine-based (content bundle), implement the smallContentAdapterInterfacefor your entity instead and persist through your own repository.- The translation only runs when the admin request carries
?translate=trueand the user holds thesulu.ai.translatepermission — the abstract subscriber checks both for you.
Permission¶
All translation actions are gated by the sulu.ai.translate security context (category Sulu AI).