Weave 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

sylius/translation-bundle

Sylius Translation Bundle adds multilingual translation support to Sylius resources, helping PHP applications manage localized entity content across locales. It provides the foundation for translatable models and translation-aware resource handling in Sylius-based projects.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Don't install it directly. sylius/translation-bundle is a READ-ONLY subtree split of the Sylius monorepo and is archived. New projects should pull sylius/sylius (or the relevant sylius/* component) and get the translation layer transitively. Adding sylius/translation-bundle to composer.json only makes sense if you are pinned to an old Sylius version where the split was a real, separately installable package.
  2. Confirm what you actually have. Run composer show sylius/translation-bundle and compare the resolved version against your sylius/sylius version. The bundle branches mirror Sylius branches, so a mismatch (e.g. bundle 1.9 against Sylius 1.12) is the first thing to rule out before debugging anything else.
  3. Look first at the mapped superclasses. The bundle's raison d'être is the TranslatableEntity / Translation mapping layer (plus the locale plumbing that hydrates currentLocale and fallbackLocale on entities). Open those classes before touching your own entities — they define the contract your resource has to satisfy.
  4. First use case: making one of your own Doctrine entities translatable. Your entity extends the translatable base (or uses the translatable trait), you define a sibling *Translation entity extending the translation base with locale + translatable associations, and you let the bundle's listener set the active/fallback locale from the request. Once that renders in two locales, you understand 90% of what the bundle does — it is small on purpose.

Implementation Patterns

Mapped-superclass resource pattern. The idiomatic shape is:

  • Product extends the translatable base, owns $translations (OneToMany, cascade={"persist","remove"}, fetch="EXTRA_LAZY") and $currentLocale / $fallbackLocale (unmapped).
  • ProductTranslation extends the translation base, maps locale (string, length 2–5) and translatable (ManyToOne back to Product), and declares a unique constraint on (translatable_id, locale).
  • Translated fields (name, description, slug) live only on the translation entity, never on the parent — this is the invariant everything else assumes.

Reading translated values. Go through the translation proxy rather than the parent: $product->translate('de_DE')->getName(), or $product->getTranslation()->getName() for the current locale. Wrap access in your own getter on the parent (returning null or a fallback) so controllers and Twig never have to know about the translation graph.

Locale wiring. The bundle's listener/setCurrentLocale()/setFallbackLocale() callbacks are what make translate() work with no arguments. In practice you set the locale from the request in a kernel request listener or via the resource bundle's context — do it in exactly one place, because the last writer wins and silent locale drift is the most common bug in this area.

Form integration. Use the bundle's translations form type (a collection keyed by locale) when you want the standard Sylius locale-tab UI. Bind it with by_reference => false on the collection and make sure the collection allow-add/allow-delete flags match your intended semantics — otherwise submitting the form wipes translations for locales that weren't rendered.

Combining with sylius/resource-bundle. The translation layer is designed to sit alongside the resource bundle's grids, factories, and controllers. If you're on modern Sylius, TranslatableTrait / AbstractTranslation from sylius/resource-bundle are the current home of this behaviour — the TranslationBundle's superclasses are the older equivalent. Pick one lineage per project and don't mix them, or you'll get duplicate translations mappings.

Repository tips. For list pages, join translations with a locale condition in a custom repository method rather than relying on lazy loading:

$qb->leftJoin('p.translations', 't', 'WITH', 't.locale = :locale')
   ->setParameter('locale', $locale);

Gotchas and Tips

  • Archived means archived. No fixes, no releases, no issue triage. If you find a bug, the only path is a PR to sylius/sylius against the matching branch — and you should budget for that not happening. Treat the bundle as frozen and plan to own any patched behaviour yourself.
  • Zero dependents on Packagist is a signal, not a stat. It reflects that everyone consumes this through the monorepo. Don't read it as "unused" and don't read it as "safe to install standalone" either.
  • Unique constraint is not optional. Without (translatable_id, locale) you'll silently accumulate duplicate translations, and translate() will return whichever row Doctrine hydrated first. Duplicates are painful to clean up later.
  • translate() can return null when no translation exists for the requested locale and no fallback is set. setFallbackLocale() is the thing that makes fallback work — if translated fields render blank on a locale you know you populated, check the fallback before you check the data.
  • Empty translation rows. Forms with the translations collection happily persist a ProductTranslation with all-null fields for every submitted locale. Filter empties in the form's onSubmit/POST_SUBMIT or in the entity setter, or your translation table grows by number_of_locales on every save.
  • Lazy-loading cost. EXTRA_LAZY on the collection helps, but iterating a grid of 50 products and calling getName() still triggers per-entity queries unless you join eagerly. Profile list endpoints specifically.
  • Adding a locale is a data migration. The bundle provides no migration tooling for it. New locale = new rows in every translation table that you care about; script it.
  • Extension points. Service definitions follow standard Sylius naming (sylius.translation.*), so decoration/overriding works as in any Symfony bundle. Do that in your app rather than vendor-patching — you cannot get a vendor patch merged here.
  • Branch alignment. If you must pin the old split package, pin it to the exact Sylius minor you run and add a composer.json conflict/constraint so a future Sylius upgrade can't silently desync the translation layer from the resource layer it depends on.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core