canabelle/cms-translation-bundle
Installation Add the bundle via Composer:
composer require canabelle/cms-translation-bundle
Enable the bundle in config/bundles.php (Symfony):
Canabelle\CMSTranslationBundle\CanabelleCMSTranslationBundle::class => ['all' => true],
Configuration Publish the default config:
php artisan vendor:publish --tag=cms-translation-config
Update config/cms_translation.php with your preferred locales (e.g., ['en', 'fr']).
First Use Case: Translating a Page
Page entity with a title field (string) and a content field (text)./**
* @ORM\Column(type="string", length=255)
* @Assert\NotBlank()
*/
private $title;
/**
* @ORM\Column(type="text")
*/
private $content;
// Add translation mappings (if using Doctrine Extensions)
public function __toString() { return $this->title; }
use Canabelle\CMSTranslationBundle\Service\TranslationService;
$translationService = $this->container->get('cms_translation.service');
$page = $translationService->getTranslatedPage($pageId, $locale);
Content Creation
en).TranslationManager to generate translation tasks:
$translationManager = $this->container->get('cms_translation.manager');
$translationManager->createTranslationTask($page, ['fr', 'es']);
Translation Workflow
TranslationListener to hook into events (e.g., postPersist):
use Canabelle\CMSTranslationBundle\Event\TranslationEvent;
public function onTranslationEvent(TranslationEvent $event) {
// Send to a translation API or queue for manual review
}
services.yaml:
services:
App\EventListener\TranslationListener:
tags:
- { name: 'kernel.event_listener', event: 'cms_translation.send', method: 'onTranslationEvent' }
Rendering Localized Content
{{ cms_translation(page, app.request.locale) }}
$translatedTitle = $translationService->translateField($page, 'title', $locale);
Gedmo\Translation, ensure the bundle’s TranslationListener doesn’t conflict with existing translation logic. Disable one or merge strategies.public function toArray($request) {
return [
'title' => $this->title->translated[$request->locale],
'content' => $this->content->translated[$request->locale],
];
}
config/cms_translation.php:
'fallback_locale' => 'en',
Outdated Dependencies
EventDispatcher changes).TranslationService to adapt to your Symfony version.Translation Field Naming
title_translations (for Gedmo\Translation). If using custom field names, configure the translation_field_suffix in the config:
'translation_field_suffix' => '_i18n',
Circular Dependencies
Page has Category, which also needs translation), the bundle may not handle this natively.TranslationStrategy for recursive entities.Performance with Large Content
$page = $entityManager->getRepository(Page::class)
->createQueryBuilder('p')
->leftJoin('p.titleTranslations', 'tt')
->where('tt.locale = :locale')
->setParameter('locale', $locale)
->getQuery()
->getOneById($pageId);
TranslationListener is registered and fired. Add logging:
public function onTranslationEvent(TranslationEvent $event) {
\Log::debug('Translation event triggered for entity:', [$event->getEntity()->getId()]);
}
locale is set in the request (e.g., via RequestStack). Override the LocaleListener if needed.Custom Translation Sources
TranslationService to support non-Doctrine sources (e.g., JSON files):
class CustomTranslationService extends TranslationService {
public function getTranslatedContent($key, $locale) {
return json_decode(file_get_contents("translations/{$locale}/{$key}.json"));
}
}
services.yaml:
services:
App\Service\CustomTranslationService:
decorates: 'cms_translation.service'
arguments: ['@custom_translation_service.inner']
Translation Validation
use Symfony\Component\Validator\Constraints as Assert;
/**
* @Assert\Length(max=500)
*/
private $content;
prePersist/preUpdate if the ValidationListener is enabled.Bulk Translation
TranslationManager to batch-create translation tasks:
$pages = $entityManager->getRepository(Page::class)->findAll();
foreach ($pages as $page) {
$translationManager->createTranslationTask($page, ['fr']);
}
How can I help you explore Laravel packages today?