ticketpark/locale-bundle
A Symfony bundle for managing application locales, tying request locale detection, routing, and translation together so your app can serve the right language to each user with minimal configuration.
Before you install anything: this bundle is archived and its last release was July 2017. Treat it as a legacy-maintenance dependency only. If you are starting a new project, the same problem is solved today by Symfony's TranslatableMessage, Doctrine's json column type plus a small value object, or a dedicated translation-storage bundle. Only reach for ticketpark/locale-bundle if you inherited a codebase that already depends on it and you need to keep it working.
Install and register (legacy Symfony 2.x/3.x style):
composer require ticketpark/locale-bundle
// app/AppKernel.php
$bundles = [
// ...
new Ticketpark\LocaleBundle\TicketparkLocaleBundle(),
];
There is no Flex recipe and no autoconfiguration for a bundle of this vintage, so on any modern Symfony (4+) you will either need to register it manually in config/bundles.php or accept that it will not boot cleanly. Check composer.json of the bundle for the exact symfony/* and php constraints before committing.
Where to look first, in this order:
Model/ (or Entity/) — find the class that actually holds the translations. This is almost always an array/map of locale => string wrapped in a value object with getters such as getTranslation($locale) and a fallback mechanism. Everything else in the bundle orbits this class.Doctrine/ or Types/ — the bundle ships a custom Doctrine type (typically named something like localized_string) that serializes the value object into a TEXT/JSON column. Confirm the exact registered type name in the bundle's extension class or Resources/config/services.yml.Twig/ — the Twig extension is your day-to-day interface. Look for the filter/function name so you can render a localized field in templates without unwrapping the object by hand.Tests/ — with a 0-star, archived package the test suite is the only reliable documentation. Read the fixtures to see the real expected storage format.First use case: add a localized name to an existing entity. Declare the column with the bundle's Doctrine type, persist a translation map, then render the current-locale value in a Twig template. Once that round-trips correctly, you understand 80% of the package.
Map the column through the custom Doctrine type. The intended pattern is to let Doctrine own the serialization rather than storing an array by hand:
/**
* @ORM\Column(type="localized_string", nullable=true)
*/
protected $name;
Persisting then becomes $product->setName(new LocalizedString(['en' => 'Shirt', 'de' => 'Hemd'])). Verify the type name against the bundle's service definition — it is not guaranteed to be localized_string.
Always read through the value object, never the raw column. Access the property via getTranslation($locale) (or whatever the accessor is called) so the bundle's fallback logic applies. If you bypass it and pull the array out of the column directly you lose fallback-to-default-locale behaviour and will render blank strings for untranslated locales.
Integrate with the request locale once, at the boundary. Resolve the locale in a kernel/request listener or from $request->getLocale(), and pass it explicitly into the value object or the Twig filter. Avoid having the bundle reach into the container for the locale from inside the model — it makes the value objects untestable and behaves badly in console commands and queue workers where there is no request.
Form handling. If the bundle ships a form type for the localized field, expect a compound or collection-style widget keyed by locale rather than a single text input. If it does not, bind a CollectionType keyed by a locale array and hydrate the value object in the form's onSubmit/data transformer. The latter is usually cleaner than depending on an unmaintained form type.
Twig rendering. Prefer a single Twig filter for output so escaping and fallback stay centralised:
{{ product.name|localized_string(app.request.locale) }}
Wrap that in one template partial for entities with several localized fields, so the locale argument is passed in one place rather than repeated across templates.
Seed and migrate in the same shape the type expects. In fixtures and migrations, write the serialized structure the Doctrine type produces (array → JSON/text). If you insert a plain PHP array via raw SQL the type will not be able to hydrate it on read; keep all writes going through the ORM, or replicate the exact serialization.
Workflow that fits this bundle: editorial translations are authored in an admin form per locale, stored in the localized column, and read back with request locale at render time. There is no extraction/import step and no integration with Symfony's translation catalogue — this bundle stores per-record localized strings, not messages.en.yaml. Keep those two systems clearly separated in your head; they solve different problems.
composer.json before touching anything, and expect Composer to refuse the install on a modern stack.TEXT/JSON frequently cause spurious doctrine:schema:validate / migrations:diff churn. Confirm the type's getSQLDeclaration and requiresSQLCommentHint settings; if the type does not return true from requiresSQLCommentHint, Doctrine may drop and recreate the column on every diff. You can work around it by adding a column comment or by overriding the type registration.null entry and a missing key often behave differently in the fallback path (and false/0 values can be swallowed by loose null checks). Normalise on write: decide whether an untranslated locale is null or simply absent, and be consistent.de_AT to a configured default locale, not through de_AT → de → en. If you need a full chain, implement it in a decorator around the value object rather than patching the bundle.Twig_Markup or similar marked-safe output, pass plain strings through it, never HTML from untrusted editors.en rendering and serve it to de visitors.final. If you must patch internals, fork the package into src/ or use a Composer replace with a vcs repository entry — never edit vendor/.getTranslation(string $locale): ?string) in your own codebase. That gives you a one-file swap to Symfony's TranslatableMessage or a JSON column when you finally remove the dependency.How can I help you explore Laravel packages today?