Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Translated Forms Laravel Package

braunstetter/translated-forms

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require braunstetter/translated-forms
    

    Ensure you also have knplabs/doctrine-behaviors installed (for translatable entities).

  2. Enable the Bundle: Add to config/bundles.php:

    Braunstetter\TranslatedFormsBundle\BraunstetterTranslatedFormsBundle::class => ['all' => true],
    
  3. First Use Case: Create a translatable entity (e.g., Post) with Translatable behavior, then mark a form as translated:

    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilderInterface;
    
    class PostType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder
                ->add('title')
                ->add('content');
        }
    
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults([
                'translated' => true, // Enable translation
                'data_class' => Post::class,
            ]);
        }
    }
    
  4. Locale Handling: Ensure your app uses a locale (e.g., via RequestStack or middleware). The bundle reads the locale from the request.


Implementation Patterns

Core Workflow

  1. Form Submission: The bundle automatically detects the current locale (e.g., fr, en) from the request and binds form data to the correct translation of the entity.

  2. Entity Structure:

    • Use Translatable behavior for your entity (e.g., Post with title and content fields).
    • Example entity:
      use Knp\DoctrineBehaviors\Model\Translatable\Translatable;
      
      class Post
      {
          use Translatable;
      
          // ...
      }
      
  3. Dynamic Field Handling:

    • Fields marked as translatable in the entity will auto-sync with the current locale.
    • Non-translatable fields (e.g., createdAt) remain unchanged.
  4. Integration with Symfony Forms:

    • Works seamlessly with FormBuilder, FormFactory, and FormEvents.
    • Example with events:
      $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
          $form = $event->getForm();
          $post = $event->getData();
          if ($post) {
              $form->add('title', TextType::class, [
                  'label' => $post->getTranslations()->getCurrentLocale('title'),
              ]);
          }
      });
      
  5. Locale Switching:

    • If the user switches locales mid-form (e.g., via a dropdown), the form will reflect the new locale on subsequent requests.
    • Example locale switcher:
      <a href="{{ path('app_switch_locale', {'_locale': 'fr'}) }}">Français</a>
      

Advanced Patterns

  1. Custom Field Mapping: Override the default field-to-translation mapping in the form type:

    $resolver->setDefaults([
        'translated' => [
            'fields' => ['custom_field_name' => 'title'], // Map form field to entity translation
        ],
    ]);
    
  2. Fallback Locales: Configure fallback locales in config/packages/braunstetter_translated_forms.yaml:

    braunstetter_translated_forms:
        fallback_locales: ['en', 'fr']
    
  3. Validation: Validate translations per locale using constraints:

    use Symfony\Component\Validator\Constraints as Assert;
    
    $builder->add('title', TextType::class, [
        'constraints' => [
            new Assert\NotBlank(),
            new Assert\Length(['min' => 3]),
        ],
    ]);
    
  4. API/JSON Forms: For API responses, ensure the correct locale is set in the request before processing:

    $request->setLocale('fr');
    $form = $this->createForm(PostType::class, $post, ['translated' => true]);
    

Gotchas and Tips

Pitfalls

  1. Locale Not Set:

    • Issue: Form fails silently if no locale is set in the request.
    • Fix: Ensure middleware or a base controller sets the locale:
      public function __construct(private RequestStack $requestStack) {}
      public function setLocale(): void
      {
          $request = $this->requestStack->getCurrentRequest();
          $request->setLocale($request->getPreferredLanguage(['en', 'fr']));
      }
      
  2. Proxy Translations Missing:

    • Issue: If Translatable behavior isn’t configured with Proxy translations, the bundle won’t work.
    • Fix: Add Proxy to your entity:
      use Knp\DoctrineBehaviors\Model\Translatable\Translation;
      
      class PostTranslation
      {
          use Translation;
      }
      
  3. Caching Conflicts:

    • Issue: Cached forms may not reflect locale changes.
    • Fix: Disable caching for translated forms or clear cache after locale changes:
      $this->get('cache')->clear();
      
  4. Nested Forms:

    • Issue: Nested translatable forms may not bind correctly.
    • Fix: Explicitly mark nested forms as translated:
      $builder->add('author', AuthorType::class, ['translated' => true]);
      
  5. Database Mismatch:

    • Issue: Form data doesn’t persist if the entity’s translated fields don’t match the form fields.
    • Fix: Verify field names in the form match the entity’s Translatable fields.

Debugging Tips

  1. Check Locale: Dump the current locale in a controller to verify it’s set:

    dd($request->getLocale()); // Should output 'fr', 'en', etc.
    
  2. Form Data Binding: Debug form binding by inspecting the entity after submission:

    $form->handleRequest($request);
    if ($form->isSubmitted() && $form->isValid()) {
        dd($post->getTranslations()->getCurrentLocale('title')); // Check if data is bound
    }
    
  3. Event Listeners: Use FormEvents::PRE_SUBMIT to log form data before submission:

    $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
        error_log(print_r($event->getData(), true));
    });
    

Extension Points

  1. Custom Translator: Override the default translator service to handle custom logic:

    # config/services.yaml
    Braunstetter\TranslatedFormsBundle\Translator\TranslatorInterface: '@app.custom_translator'
    
  2. Dynamic Field Labels: Use FormEvents::PRE_SET_DATA to dynamically set labels based on locale:

    $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
        $form = $event->getForm();
        $form->get('title')->setLabel(__('Title', [], $event->getRequest()->getLocale()));
    });
    
  3. Locale-Aware Validation: Add locale-specific validation rules:

    $builder->add('title', TextType::class, [
        'constraints' => [
            new Assert\Length([
                'min' => $locale === 'fr' ? 5 : 3,
                'minMessage' => '{{ locale }}: Minimum length is {{ limit }} characters.',
            ]),
        ],
    ]);
    
  4. Fallback Logic: Implement custom fallback logic for missing translations:

    $resolver->setDefaults([
        'translated' => [
            'fallback_locale' => 'en',
            'fallback_on_missing' => true,
        ],
    ]);
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
daikazu/eloquent-salesforce-objects
unseen-codes/chat
romalytar/yammi-jobs-monitoring-laravel
kisame76/filament-db-table-state
nqxcode/laravel-lucene-search
dpfx/laravel-livewire-wizards
workos/workos-php-laravel
sofa/laravel-global-scope
nawasara/auth-primitives
adhocrat-io/arkhe-main
make-dev/orca-harpoon
itsemon245/lamet
baks-dev/dashboard
amoifr/pickle-panther-bundle
make-dev/orca
dmstr/symfony-system-resources-bundle
dmstr/symfony-job-queue-bundle
dmstr/openapi-json-schema-bundle
dmstr/keycloak-security-bundle
dmstr/doctrine-audit-log-bundle