Installation:
composer require cypresslab/translation-bundle
Add the bundle to config/bundles.php:
return [
// ...
Cypress\TranslationBundle\CypressTranslationBundle::class => ['all' => true],
];
Configure the Bundle:
Update config/packages/translation.yaml (or create it):
cypress_translation:
default_locale: 'en'
available_locales: ['en', 'es', 'it']
First Use Case:
Book model:
php bin/console generate:translation:entity Book
BookTranslation extending TranslationEntity with a ManyToOne relation to Book.Basic Usage:
// Set translations
$book = new Book();
$book->setTitle('The Lord of the Rings');
$book->setTitleEs('El señor de los anillos');
$book->setTitleIt('Il signore degli anelli');
// Retrieve translated title (auto-detects locale)
$title = $book->getTitle(); // Returns 'The Lord of the Rings' if default locale is 'en'
Translation Entity Structure:
TranslationEntity for each translatable entity (e.g., BookTranslation).$object as a ManyToOne relation to the parent entity (e.g., Book).titleEs, titleIt) or use dynamic getters/setters./**
* @ORM\Entity
* @ORM\Table(name="book_translation")
*/
class BookTranslation extends TranslationEntity
{
/**
* @ORM\ManyToOne(targetEntity="Book", inversedBy="translations")
* @ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
protected $object;
// Dynamic getters/setters for translations
public function setTitleEs($title) { $this->titleEs = $title; }
public function getTitleEs() { return $this->titleEs; }
}
Parent Entity Integration:
OneToMany relation in the parent entity (e.g., Book) to its translation entity.prePersist, preUpdate) to sync translations./**
* @ORM\OneToMany(targetEntity="BookTranslation", mappedBy="object", cascade={"persist", "remove"})
*/
private $translations;
public function __construct() {
$this->translations = new ArrayCollection();
}
public function addTranslation(BookTranslation $translation) {
$this->translations[] = $translation;
return $this;
}
Adding Translations:
TranslationManager service to handle bulk operations:
$manager = $this->container->get('cypress_translation.manager');
$manager->addTranslation($book, 'title', 'es', 'El señor de los anillos');
Fetching Translations:
<h1>{{ book|translate('title') }}</h1>
<h1>{{ book|translate('title', 'es') }}</h1>
translation.yaml:
cypress_translation:
fallback_locales: ['en']
Bulk Updates:
php bin/console doctrine:query-sql "UPDATE book_translation SET title_es = 'Nuevo título' WHERE book_id = 1"
Validation:
NotBlank for required translations):
/**
* @Assert\NotBlank(groups={"es"})
*/
private $titleEs;
Form Handling:
FormBuilder to dynamically generate translation fields:
$builder->add('title', TextType::class, [
'translation' => true,
'locales' => ['es', 'it'],
]);
API Responses:
#[Serializer\SerializedName('title')]
public function getTranslatedTitle(): string {
return $this->getTitle($this->getLocale());
}
Admin Interfaces:
$admin->addField('title', 'string', [
'translation' => true,
'locales' => $this->getConfigurationPool()->getContainer()->getParameter('cypress_translation.available_locales'),
]);
Testing:
$this->container->get('cypress_translation.manager')->setCurrentLocale('es');
$this->assertEquals('El señor de los anillos', $book->getTitle());
Locale Mismatch:
$request->setLocale($request->getPreferredLanguage(['en', 'es', 'it']));
Circular References:
@ORM\BackUpTargetEntity or lazy loading:
#[ORM\BackUpTargetEntity(BookTranslation::class)]
private $translations;
Performance:
$qb = $this->createQueryBuilder('b')
->leftJoin('b.translations', 't')
->where('t.locale = :locale')
->setParameter('locale', $locale);
Dynamic Getters/Setters:
getTitleEs() instead of getEs()).Missing Translations:
$book->getTranslations()->count(); // Should return > 0
TranslationManager.Twig Filter Issues:
TranslationExtension is registered in your Twig environment.{{ dump(book.getTranslations()) }}
Database Schema:
php bin/console doctrine:schema:update --force
Default Locale:
# config/services.yaml
services:
App\EventListener\LocaleListener:
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
public function onKernelRequest(GetResponseEvent $event) {
$request = $event->getRequest();
$this->container->get('cypress_translation.manager')->setCurrentLocale($request->get('locale', 'en'));
}
Available Locales:
available_locales in translation.yaml matches the locales used in your entity (e.g., titleEs implies es is available).Caching:
php bin/console cache:clear
Custom Translation Logic:
TranslationEntity class to add custom behavior (e.g., validation, hooks):
class CustomTranslationEntity extends TranslationEntity {
public function prePersist() {
if (empty($this->titleEs)) {
throw new \RuntimeException('Spanish translation is required.');
}
}
}
Event Listeners:
cypress_translation.pre_translate):
$eventDispatcher->addListener('cypress_translation.pre_translate', function (TranslationEvent $event) {
if ($event->getLocale() === 'es' && empty($event->getTranslation())) {
$event->setTranslation('Fallback: ' . $event->getDefault
How can I help you explore Laravel packages today?