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

Object Translation Bundle Laravel Package

skippy565/object-translation-bundle

A Symfony bundle for locale-aware translation of object properties, helping you store and retrieve localized field values without duplicating entities. Useful for multilingual content and simple i18n workflows.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a small, low-traffic Symfony bundle (last release June 2018, 2 stars, zero dependents), so treat the README as the primary — and possibly only — source of truth. Budget time to read the code before wiring it in; there is no community knowledge base to fall back on.

Minimal setup:

composer require skippy565/object-translation-bundle

Then register it. On Symfony 3.x / 4.0–4.3 (the era this targets) that means AppKernel::registerBundles(); if you're on a Flex app, add it to config/bundles.php. Check composer.json first — the symfony/* and php constraints will tell you whether this installs at all on your stack. If you're on Symfony 5+, expect to need --ignore-platform-reqs at best and real code changes at worst.

Where to look first, in order:

  1. DependencyInjection/Configuration.php — the full config tree with defaults. This is the fastest way to learn what's configurable.
  2. Resources/config/services.* — service IDs, aliases, and which class actually does the translation work.
  3. DependencyInjection/*Extension.php — how config maps onto services (and which services are conditional).
  4. Any Entity/, Model/, or Annotation/ directory — this is where the translation storage and the @Translatable-style markers live.
  5. README.md last, so you can tell which parts are stale.

First use case to prove out: pick one entity with a single translated string field (a product name, a category label), configure it for two locales, and read it back under each locale in a controller. That exercises the whole path — metadata discovery, translation lookup, locale resolution — without you committing to a schema-wide refactor. Once that round-trips, expand field by field.

If the bundle turns out to be a thin wrapper over an existing approach (Doctrine event listeners plus a translation table), the codebase is probably small enough to read end-to-end in an afternoon. Do that before designing around it.

Implementation Patterns

Service-layer translation. The usual shape for a bundle like this is a manager/translator service you inject and call with an object plus a field (or access it via a magic getter / proxy object). Prefer injecting the named service over reaching into the container, and prefer a narrow wrapper of your own around it — that wrapper is your escape hatch when the bundle is abandoned.

class ProductPresenter
{
    public function __construct(private ObjectTranslator $translator) {}

    public function name(Product $product, string $locale): string
    {
        return $this->translator->translate($product, 'name', $locale);
    }
}

Locale is explicit, not ambient. Pass the locale into your translation calls rather than relying on the request's locale inside the domain layer. You will want to render a page in one locale while emailing a customer in another, and request-scoped locale resolution makes that painful.

Write path. Translation bundles almost always separate read from write: you have a setTranslation($object, $field, $locale, $value) (or an attached translation entity) that must be persisted through Doctrine. Establish one service method that does "set + flush" and one that does "set only," so batch imports don't flush per field.

Fallback chain. Configure a fallback locale (e.g. en → de) and decide early whether a missing translation returns null, an empty string, or the fallback value. Surface that decision in your own wrapper so the rest of the app doesn't have to care.

Form and admin integration. The typical pattern is an embedded collection of translation forms, one sub-form per locale, bound to the translation entity or to a DTO. If the bundle ships form types, use them; if it doesn't, build a TranslationType per locale and map it onto the write path above. Keep the admin editing surface separate from the front-end read path — the read path should never depend on form classes.

Twig. If a Twig extension is registered, use it for read-only display ({{ product|trans_field('name') }}), but keep any logic-dependent formatting in PHP. Check Resources/config/ for the extension service and its filter name — it won't necessarily be trans.

Bulk reads. Any per-object translator call risks an N+1 against the translation table. For listings, prefetch translations for the whole result set in one query (usually a WHERE object_class = ? AND object_id IN (?) AND locale = ?) and hydrate from that map. If the bundle has no batch API, write one against its repository — this is the single most valuable optimization you'll make.

Gotchas and Tips

  • Maintenance risk is the headline. Last release 2018, 2 stars, 0 dependents, no tests to lean on. Plan for ownership: fork it, vendor it, or write a 150-line replacement if the feature set is small. Don't let it become load-bearing without a fork under your org.
  • Symfony version drift. Expect services.yml schema changes, removed DefinitionDecorator, and PHP 8 deprecations (dynamic properties, ${var} in strings). Run the code under your actual PHP version early; static analysis (phpstan at level 5+) will find most of the rot fast.
  • Annotation vs. attributes. If translatable fields are marked with Doctrine annotations, PHP 8 attributes won't work unless the bundle reads both. Check the metadata driver before annotating a hundred entities.
  • Proxy/identity surprises. Translating an object often means returning a decorated or proxied instance. instanceof and === checks against the original entity can then behave unexpectedly — especially inside Doctrine's identity map and with spl_object_id. Keep translated values as strings; don't pass translated objects into EntityManager::persist().
  • Detached and unserialized entities. Translation lookups keyed on object_class + object_id fail for objects that aren't persisted yet (no ID) or have been detached. Guard the write path so translations are attached after the entity is flushed.
  • Cache invalidation. If the bundle caches translations per request or per locale, changing a translation in the admin won't be visible until the cache clears. Look for a cache service in services.* and either disable it in dev or wire an invalidation call into your save path.
  • Config quirks. The extension may silently ignore unknown keys or apply defaults you didn't expect (e.g. default locale falling back to %kernel.default_locale%). Dump the processed config (bin/console debug:config <alias>) rather than reading the YAML and assuming.
  • Debugging entry points. bin/console debug:container <service-id>, debug:event-dispatcher (look for Doctrine lifecycle listeners), and the profiler's Doctrine panel to confirm the translation query count. If translations "aren't saving," check the listener is registered before blaming the bundle.
  • Extension points to prefer. Decorate the translator service, or implement the bundle's interface (if one exists) and override the alias in your app config. Avoid editing vendor code; a fork plus a Composer repositories VCS entry is cleaner and keeps your diff reviewable.
  • Test the fallbacks explicitly. Missing locale, empty string, and locale not in your configured list are the three cases that break in production. Write one integration test per case against your wrapper — it doubles as your upgrade safety net if you ever swap the implementation.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource