a2lix/translation-form-bundle
Installation:
composer require a2lix/translation-form-bundle
Add to config/bundles.php:
return [
// ...
A2lix\TranslationFormBundle\A2lixTranslationFormBundle::class => ['all' => true],
];
Enable Translations:
Configure your Doctrine entities with translation behavior (e.g., Gedmo\Translation or Knp\DoctrineBehaviors\Translatable). Example:
use Gedmo\Mapping\Annotation as Gedmo;
/**
* @Gedmo\TranslationEntity(class="App\Entity\ProductTranslation")
*/
class Product {}
First Form Integration:
Use TranslationsFormsType in your form builder:
use A2lix\TranslationFormBundle\Form\Type\TranslationsFormsType;
$builder->add('translations', TranslationsFormsType::class, [
'entity' => $product,
'fields' => ['name', 'description'], // Fields to translate
]);
Twig Integration: Add to your Twig templates:
{% form_theme form _self %}
{% block a2lix_translation_form_widget %}
{{ form_widget(form) }}
{% endblock %}
Create a form for a translatable entity (e.g., Product) with fields name and description:
use Symfony\Component\Form\FormBuilderInterface;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('translations', TranslationsFormsType::class, [
'entity' => $product,
'fields' => ['name', 'description'],
'locales' => ['en', 'fr'], // Optional: restrict locales
]);
}
Form Submission: Handle submission with validation:
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$product = $form->getData();
$em->persist($product);
$em->flush();
}
Dynamic Locale Handling:
Use TranslatedEntityType for choice fields with translated labels:
$builder->add('category', TranslatedEntityType::class, [
'class' => Category::class,
'choice_label' => 'name', // Field to translate for labels
]);
Partial Updates: Update only specific translations:
$builder->add('translations', TranslationsFormsType::class, [
'entity' => $product,
'fields' => ['description'], // Only update 'description'
'locales' => ['fr'], // Only French translations
]);
Symfony UX: Combine with Symfony UX for reactive forms:
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
#[AsLiveComponent('translation_form')]
class TranslationForm extends AbstractType {}
API Platform: Use with API Platform for RESTful translation endpoints:
# config/packages/api_platform.yaml
api_platform:
formats:
jsonld:
mime_types: ['application/ld+json']
translation_form: true
Custom Validation: Add constraints to translation fields:
use Symfony\Component\Validator\Constraints as Assert;
$builder->add('translations.name', null, [
'constraints' => [
new Assert\Length(['min' => 3]),
],
]);
Locale Mismatch:
Ensure locales in TranslationsFormsType match your app’s supported locales (from framework.default_locale or A2lixTranslationFormBundle config).
Fix: Validate locales in config/packages/a2lix_translation_form.yaml:
a2lix_translation_form:
locales: ['en', 'fr', 'de']
Circular References:
Avoid circular references in translatable entities (e.g., Product translating Category which translates Product). Use @ORM\Transient or lazy loading.
Fix: Add @ORM\Transient to the inverse side:
/**
* @ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* @ORM\Transient
*/
private $category;
Form Theme Conflicts:
Override Twig blocks carefully. If a2lix_translation_form_widget breaks, check for conflicting form themes.
Fix: Extend the base theme:
{% extends 'A2lixTranslationFormBundle:Form:fields.html.twig' %}
{% block a2lix_translation_form_widget %}
{{ form_widget(form) }}
{# Custom additions #}
{% endblock %}
Missing Translations: If translations aren’t saved, verify:
prePersist, preUpdate) are registered.Gedmo\Handler\TranslatableListener or Knp\DoctrineBehaviors\ORM\EventListener\TranslatableListener is enabled.
Debug: Check Symfony profiler’s "Doctrine" tab for translation events.Form Field Not Rendering:
Ensure the field exists in the entity’s translation mapping (e.g., @Gedmo\Translatable(fields={"name", "description"})).
Debug: Dump the form’s children:
dump($form->all());
Custom Field Types:
Extend TranslationsFormsType for custom logic:
use A2lix\TranslationFormBundle\Form\Type\TranslationsFormsType;
class CustomTranslationsType extends TranslationsFormsType {
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults([
'custom_option' => true,
]);
}
}
Twig Extensions: Add custom Twig filters for translations:
// src/Twig/AppExtension.php
class AppExtension extends \Twig\Extension\AbstractExtension {
public function getFilters() {
return [
new \Twig\TwigFilter('translate_field', [$this, 'translateField']),
];
}
public function translateField($entity, $field, $locale) {
return $entity->getTranslation($field, $locale);
}
}
Event Listeners: Hook into translation events for custom logic:
// src/EventListener/TranslationListener.php
class TranslationListener implements EventSubscriber {
public static function getSubscribedEvents() {
return [
TranslatableListener::PRE_TRANSLATE => 'onPreTranslate',
];
}
public function onPreTranslate(TranslatableEvent $event) {
// Modify translations before save
}
}
Default Locale Fallback:
Configure fallback locales in config/packages/a2lix_translation_form.yaml:
a2lix_translation_form:
fallback_locale: 'en'
locales: ['en', 'fr']
Dynamic Locale Selection:
Use locale_chooser to let users select locales dynamically:
$builder->add('translations', TranslationsFormsType::class, [
'locale_chooser' => true,
]);
Excluded Fields: Exclude fields from translation forms:
$builder->add('translations', TranslationsFormsType::class, [
'exclude_fields' => ['createdAt', 'updatedAt'],
]);
How can I help you explore Laravel packages today?