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

Translation Bundle Laravel Package

cypresslab/translation-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require cypresslab/translation-bundle
    

    Add the bundle to config/bundles.php:

    return [
        // ...
        Cypress\TranslationBundle\CypressTranslationBundle::class => ['all' => true],
    ];
    
  2. Configure the Bundle: Update config/packages/translation.yaml (or create it):

    cypress_translation:
        default_locale: 'en'
        available_locales: ['en', 'es', 'it']
    
  3. First Use Case:

    • Generate a translation entity for your Book model:
      php bin/console generate:translation:entity Book
      
    • This creates BookTranslation extending TranslationEntity with a ManyToOne relation to Book.
  4. 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'
    

Implementation Patterns

Entity Design

  1. Translation Entity Structure:

    • Extend TranslationEntity for each translatable entity (e.g., BookTranslation).
    • Define $object as a ManyToOne relation to the parent entity (e.g., Book).
    • Add locale-specific properties (e.g., 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; }
    }
    
  2. Parent Entity Integration:

    • Add a OneToMany relation in the parent entity (e.g., Book) to its translation entity.
    • Use lifecycle callbacks (e.g., 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;
    }
    

Workflows

  1. Adding Translations:

    • Use the TranslationManager service to handle bulk operations:
      $manager = $this->container->get('cypress_translation.manager');
      $manager->addTranslation($book, 'title', 'es', 'El señor de los anillos');
      
  2. Fetching Translations:

    • Auto-Locale Detection:
      <h1>{{ book|translate('title') }}</h1>
      
    • Explicit Locale:
      <h1>{{ book|translate('title', 'es') }}</h1>
      
    • Fallback Logic: Configure fallback locales in translation.yaml:
      cypress_translation:
          fallback_locales: ['en']
      
  3. Bulk Updates:

    • Use Doctrine events or commands to update translations for existing entities:
      php bin/console doctrine:query-sql "UPDATE book_translation SET title_es = 'Nuevo título' WHERE book_id = 1"
      
  4. Validation:

    • Add constraints to translation fields (e.g., NotBlank for required translations):
      /**
       * @Assert\NotBlank(groups={"es"})
       */
      private $titleEs;
      

Integration Tips

  1. Form Handling:

    • Use Symfony’s FormBuilder to dynamically generate translation fields:
      $builder->add('title', TextType::class, [
          'translation' => true,
          'locales' => ['es', 'it'],
      ]);
      
  2. API Responses:

    • Serialize translations with API Platform or FOSRest:
      #[Serializer\SerializedName('title')]
      public function getTranslatedTitle(): string {
          return $this->getTitle($this->getLocale());
      }
      
  3. Admin Interfaces:

    • Integrate with SonataAdmin or EasyAdmin for in-context translation editing:
      $admin->addField('title', 'string', [
          'translation' => true,
          'locales' => $this->getConfigurationPool()->getContainer()->getParameter('cypress_translation.available_locales'),
      ]);
      
  4. Testing:

    • Mock translations in PHPUnit:
      $this->container->get('cypress_translation.manager')->setCurrentLocale('es');
      $this->assertEquals('El señor de los anillos', $book->getTitle());
      

Gotchas and Tips

Pitfalls

  1. Locale Mismatch:

    • Issue: Translations may not render if the locale is not explicitly set (e.g., in API requests).
    • Fix: Ensure the locale is set via middleware or request listener:
      $request->setLocale($request->getPreferredLanguage(['en', 'es', 'it']));
      
  2. Circular References:

    • Issue: Bidirectional relations between parent and translation entities can cause serialization issues.
    • Fix: Use @ORM\BackUpTargetEntity or lazy loading:
      #[ORM\BackUpTargetEntity(BookTranslation::class)]
      private $translations;
      
  3. Performance:

    • Issue: N+1 queries when fetching translations for multiple entities.
    • Fix: Use DQL or QueryBuilder with joins:
      $qb = $this->createQueryBuilder('b')
          ->leftJoin('b.translations', 't')
          ->where('t.locale = :locale')
          ->setParameter('locale', $locale);
      
  4. Dynamic Getters/Setters:

    • Issue: Magic methods may conflict with existing methods.
    • Fix: Prefix translation methods (e.g., getTitleEs() instead of getEs()).

Debugging

  1. Missing Translations:

    • Check if the translation entity is properly associated with the parent entity:
      $book->getTranslations()->count(); // Should return > 0
      
    • Verify the locale is correctly set in the TranslationManager.
  2. Twig Filter Issues:

    • Ensure the TranslationExtension is registered in your Twig environment.
    • Debug with:
      {{ dump(book.getTranslations()) }}
      
  3. Database Schema:

    • After generating translation entities, run:
      php bin/console doctrine:schema:update --force
      

Configuration Quirks

  1. Default Locale:

    • Override the default locale dynamically (e.g., per user):
      # 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'));
      }
      
  2. Available Locales:

    • Ensure available_locales in translation.yaml matches the locales used in your entity (e.g., titleEs implies es is available).
  3. Caching:

    • Clear the cache after changing translation configurations:
      php bin/console cache:clear
      

Extension Points

  1. Custom Translation Logic:

    • Override the 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.');
              }
          }
      }
      
  2. Event Listeners:

    • Listen to translation-related events (e.g., 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
      
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.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle