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

Entity Translations Bundle Laravel Package

arxy/entity-translations-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require arxy/entity-translations-bundle
    

    Register the bundle in config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 3):

    Arxy\EntityTranslationsBundle\ArxyEntityTranslationsBundle::class => ['all' => true],
    
  2. Configure Doctrine Add to config/packages/doctrine.yaml:

    doctrine:
        orm:
            resolve_target_entities:
                Arxy\EntityTranslationsBundle\Model\Language: App\Entity\Language
    
  3. Define Core Entities

    • Language Entity (implements \Arxy\EntityTranslationsBundle\Model\Language):
      #[ORM\Entity]
      class Language implements LanguageInterface
      {
          #[ORM\Id, ORM\Column(type: 'string', length: 5)]
          private string $locale;
      }
      
    • Translatable Entity (implements \Arxy\EntityTranslationsBundle\Model\Translatable):
      #[ORM\Entity]
      class Product implements Translatable
      {
          #[ORM\OneToMany(targetEntity: ProductTranslation::class, mappedBy: 'translatable', cascade: ['ALL'], orphanRemoval: true)]
          private Collection $translations;
      
          public function __construct() {
              $this->translations = new ArrayCollection();
          }
      
          public function addTranslation(ProductTranslation $translation): void {
              $this->translations->add($translation);
              $translation->setTranslatable($this);
          }
      
          public function removeTranslation(ProductTranslation $translation): void {
              $this->translations->removeElement($translation);
          }
      
          public function setCurrentTranslation(?Translation $translation): void {
              // Used internally by the bundle
          }
      }
      
    • Translation Entity (implements \Arxy\EntityTranslationsBundle\Model\Translation):
      #[ORM\Entity]
      class ProductTranslation implements Translation
      {
          #[ORM\Id, ORM\ManyToOne(targetEntity: Product::class, inversedBy: 'translations')]
          private ?Product $translatable;
      
          #[ORM\Id, ORM\ManyToOne(targetEntity: Language::class)]
          private ?Language $language;
      
          #[ORM\Column(type: 'string')]
          private string $name;
      }
      
  4. First Usage Inject the translator service and translate an entity:

    use Arxy\EntityTranslationsBundle\Translator;
    
    public function __construct(private Translator $translator) {}
    
    public function showProduct(Product $product, string $locale = 'en') {
        $this->translator->initializeTranslation($product, $locale);
        return $product->getName(); // Returns translated name
    }
    

Implementation Patterns

Core Workflows

1. Entity Translation Management

  • Initializing Translations:
    $this->translator->initializeTranslation($entity, 'fr');
    // Automatically sets $entity->setCurrentTranslation() and falls back to configured locales if needed.
    
  • Direct Field Translation:
    $translatedName = $this->translator->translate($product, 'name', 'es');
    // Bypasses entity methods; useful for dynamic fields.
    

2. Form Integration

  • Create a Translation Form Type:
    class ProductTranslationType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options) {
            $builder->add('name', TextType::class, [
                'constraints' => [
                    new NotBlank(['groups' => ['en']]), // English is required
                    new Length(['max' => 100, 'groups' => ['fr']]), // French has length constraint
                ],
            ]);
        }
    
        public function configureOptions(OptionsResolver $resolver) {
            $resolver->setDefaults(['data_class' => ProductTranslation::class]);
        }
    }
    
  • Embed in Parent Form:
    $builder->add('translations', TranslationsType::class, [
        'entry_type' => ProductTranslationType::class,
        'em' => 'doctrine.orm.entity_manager',
        'entry_language_options' => [
            'en' => ['required' => true],
            'fr' => ['required' => false],
        ],
    ]);
    
  • Customize UI with Twig Themes:
    # config/packages/twig.yaml
    twig:
        form_themes: ['@ArxyEntityTranslations/bootstrap_4_tab_layout.html.twig']
    

3. Twig Integration

  • Translate Fields:
    {{ product|translate('name') }} {# Uses current locale #}
    {{ product|translate('name', 'de') }} {# Forces German #}
    
  • Access Full Translation:
    {% set translation = product|translation('ja') %}
    {% if translation %}
        {{ translation.name }} {# Japanese name #}
    {% endif %}
    

4. Locale Management

  • Set Global Locale:
    $this->translator->setLocale('pt_BR');
    // Affects all managed entities.
    
  • Detach Entity from Locale Management:
    $this->translator->detach($product);
    // Prevents automatic translation updates.
    

5. Validation by Locale

  • Locale-Specific Constraints:
    $builder->add('description', TextType::class, [
        'constraints' => [
            new NotBlank(['groups' => ['en']]),
            new Length(['max' => 500, 'groups' => ['es']]),
        ],
    ]);
    
  • Form Submission: The bundle automatically validates constraints based on the submitted locale.

Integration Tips

Doctrine Events

  • Post-Persist/Update: The bundle automatically loads translations for persisted entities. For custom logic:
    $entityManager->getEventManager()->addEventListener(
        ORM\Events::postPersist,
        function (ORM\Event\LifecycleEventArgs $args) {
            $entity = $args->getObject();
            if ($entity instanceof Translatable) {
                $this->translator->initializeTranslation($entity);
            }
        }
    );
    

API Responses

  • Serialize Translations: Use Symfony Serializer with custom normalization:
    #[Serializer\SerializedName('name')]
    public function getName(): ?string {
        return $this->currentTranslation?->getName();
    }
    

Testing

  • Mock Translator:
    $translator = $this->createMock(Translator::class);
    $translator->method('translate')->willReturn('Mocked Name');
    $this->container->set(Translator::class, $translator);
    

Gotchas and Tips

Pitfalls

1. Missing setCurrentTranslation

  • Error: Call to undefined method App\Entity\Product::setCurrentTranslation().
  • Fix: Ensure your Translatable entity implements:
    public function setCurrentTranslation(?Translation $translation): void {}
    

2. Form Validation Fails

  • Issue: Locale-specific constraints (e.g., groups: ['en']) are ignored.
  • Root Cause: The TranslationsType requires explicit required: true in entry_language_options.
  • Fix:
    entry_language_options:
        en: { required: true }  # Explicitly mark as required
    

3. Circular References in Forms

  • Error: Cannot determine which entity class to use for form type "...".
  • Fix: Ensure data_class is set in your TranslationType:
    $resolver->setDefault('data_class', ProductTranslation::class);
    

4. Locale Fallback Not Working

  • Issue: initializeTranslation() returns a fallback locale (e.g., en instead of fr).
  • Debug: Check framework.translator.fallback_locales in config/packages/framework.yaml:
    framework:
        translator:
            fallbacks: ["en"]  # Ensure fallback is configured
    

5. Performance with Large Datasets

  • Problem: Querying translations for many entities causes N+1 queries.
  • Solution: Use DQL or QueryBuilder with JOIN:
    $qb = $entityManager->createQueryBuilder()
        ->select('p, t')
        ->from(Product::class, 'p')
        ->leftJoin('p.translations', 't')
        ->where('t.language = :locale')
        ->setParameter('locale', 'fr');
    

Debugging Tips

1. Check Initialized Locale

  • Method:
    $locale = $this->translator->initializeTranslation($product, 'it');
    // Log
    
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.
comsave/common
alecsammon/php-raml-parser
chrome-php/wrench
lendable/composer-license-checker
typhoon/reflection
mesilov/moneyphp-percentage
mike42/gfx-php
bookdown/themes
aura/view
aura/html
aura/cli
povils/phpmnd
nayjest/manipulator
omnipay/tests
psr-mock/http-message-implementation
psr-mock/http-factory-implementation
psr-mock/http-client-implementation
voku/email-check
voku/urlify
rtheunissen/guzzle-log-middleware