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

Translations Bundle Laravel Package

ongr/translations-bundle

ONGR TranslationsBundle adds database-backed translation management to Symfony apps, letting you store, edit, and organize translation messages as Doctrine entities instead of flat files, with import/export support for existing translations.

View on GitHub
Deep Wiki
Context7

Getting Started

ongr/translations-bundle stores Symfony translation catalogues in Elasticsearch and feeds them back into the standard translator service, so {{ 'checkout.title'|trans }} keeps working unchanged while the source of truth moves out of YAML/XLIFF files and into an index. It sits on top of ongr/elasticsearch-bundle, which means the real prerequisite is an ONGR/Elasticsearch stack, not just Symfony.

Minimum steps:

  1. Pin the environment first. This package's last release is from July 2016. Expect Symfony 2.8/3.x, PHP 5.5–7.0-era code, and Elasticsearch 1.x/2.x semantics (mapping types, string fields). Do not attempt this on Symfony 4+ or ES 6+ without a fork.

  2. Install the stack, not just the bundle:

    composer require ongr/elasticsearch-bundle ongr/translations-bundle
    
  3. Register the bundles in AppKernel, ONGR first:

    new ONGR\ElasticsearchBundle\ONGRElasticsearchBundle(),
    new ONGR\TranslationsBundle\ONGRTranslationsBundle(),
    
  4. Configure a manager and the managed locales:

    # app/config/config.yml
    ongr_elasticsearch:
        managers:
            default:
                index:
                    index: app_translations
                    hosts: ['%elasticsearch_host%']
                mappings:
                    - ONGRTranslationsBundle
    
    ongr_translations:
        managed_locales: ['en', 'de', 'fr']
    
  5. Create the index, then import your existing files:

    app/console ongr:es:index:create --manager=default
    app/console ongr:translations:import --help   # confirm flags in your version
    app/console ongr:translations:import --path=app/Resources/translations
    
  6. Verify the wiring before writing any code: app/console debug:translation en AppBundle should now show values coming from the index, and app/console debug:container | grep ongr_translations tells you which loader/services the bundle actually registered in your version. Service IDs drifted between 2015–2016 releases; trust the container over the README.

First real use case: a site where translators edit copy in a browser (the ONGR sandbox-style admin, or your own CRUD over the Translation document), changes land in ES immediately, and the next request renders the new copy without a deploy. That is the entire value proposition — if you don't need runtime-editable translations, you don't need this package.

Implementation Patterns

Files in Git as source of truth, ES as runtime store. The most maintainable pattern with a package this old is to keep YAML/XLIFF in the repo, import them as a deploy step, and never hand-edit the index in production:

# deploy.sh
app/console ongr:es:index:create --manager=default --if-not-exists
app/console ongr:translations:import --path=app/Resources/translations --force
app/console cache:clear --env=prod

Go the other direction (ongr:translations:export) when translators have been working directly in the admin UI, so their changes get committed and reviewed like any other diff.

Rebuild the index rather than mutate it. Because imports generally upsert by translation key, keys deleted from your files tend to linger as orphans. For a clean state, drop and recreate the index on deploy (or on a versioned alias) and re-import. This also solves mapping changes, which ES 1.x/2.x cannot do in place.

Use domains deliberately. The loader is queried per locale (and per domain, depending on version), so keep messages, validators, and any custom domains separate. Don't dump everything into messages — it makes both the loader payload and the admin UI unusable.

Programmatic editing. For a custom admin controller, inject the document manager and work with the Translation document (key as _id, an array of locale/value pairs):

$dm = $this->get('ongr_elasticsearch.manager.default');
$repo = $dm->getRepository('ONGRTranslationsBundle:Translation');
$translation = $repo->find('checkout.title');
$translation->addTranslation('de', 'Zur Kasse');
$dm->persist($translation);
$dm->commit();

Refresh the index ($dm->refresh(), or wait for the 1s refresh interval) before asserting in tests, otherwise reads immediately after writes return stale data.

Locale fallback awareness. Symfony's fallback chain means a request for de_AT may load de and then en. Every locale in that chain must exist in managed_locales and must have been imported, or you'll get half-translated pages with no error.

Decorating the loader for caching. The biggest day-to-day integration tip: wrap the ES-backed loader in a caching decorator keyed by locale + domain, backed by your app cache pool. This turns a per-request ES round-trip into a cache hit and is the single change that makes the package viable under load.

services:
    app.translation.loader.cached:
        class: AppBundle\Translation\CachedLoader
        decorates: <the loader service id from debug:container>
        arguments: ['@app.translation.loader.cached.inner', '@cache.app']

Gotchas and Tips

  • It is effectively abandoned. Five stars, zero dependents, last release 2016. Treat every README instruction as a hypothesis to verify against debug:container and --help. Budget time for reading the bundle source; it's small.
  • No modern ES support. Analyzed string fields, mapping types, and transport assumptions from ES 1.x/2.x will fail on ES 6+. This is a hard blocker, not a config quirk.
  • Key matching depends on the mapping. Translation keys are document IDs, so exact lookups are fine, but any querying/filtering you do in the admin UI needs index: not_analyzed on the key field, or checkout.title and checkout title collide.
  • The loader is not free. Depending on the version, a catalogue load can pull the whole locale in one shot. Large catalogues (tens of thousands of keys) will show up as latency in the profiler under the translation panel — measure with the Symfony profiler before blaming ES.
  • cache:clear is not optional. Symfony caches the container and, in some configurations, compiled translation resources. A deploy that imports but doesn't clear can serve yesterday's copy from the app cache.
  • debug:translation is your best debugging tool. It reads through the real translator, so it reflects exactly what ES is serving — including missing keys (shown as falls back to the key itself). Use it instead of curl-ing the index when a string "isn't translating".
  • Inspect the raw index when in doubt: curl 'localhost:9200/app_translations/_search?pretty&size=5'. If the document isn't there, the problem is the import command, not the translator.
  • Placeholders and pluralization survive, because the loader builds a normal MessageCatalogue from stored strings — but transChoice plural forms are resolved at import time from your XLIFF, so editing a pluralized message in the admin UI typically edits one variant only. Keep pluralized strings in files.
  • Missing-key monitoring. Log catalogue misses in a listener (TranslatorInterface + getCatalogue()->has()), otherwise untranslated keys silently ship to production.
  • Extension points are limited but real: decorate the loader (caching, fallback to files), swap the document class in the mapping for extra fields (tags, screenshots, character limits for translators), or replace the whole thing with a Doctrine-backed loader if ES is overkill — the loader contract is the only thing Symfony actually cares about.
  • Functional tests need a live ES node. There's no meaningful in-memory mode; stub the loader for unit tests and reserve the real index for a CI job with an ES container.
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.
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics
robbiep/afterthedeadline
frankkessler/incontact-laravel-oauth2-rest
jlxh/laravel-userlog
davidmgilo/timetables
qylinfly/role-permission
reflect-digital/conduit
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr