mablae/gettext-bundle
mablae/gettext-bundle integrates GNU gettext with Symfony's translation system, letting PHP applications use .po and .mo catalogs and native gettext-style lookups alongside existing translator workflows. It helps projects reuse established gettext tooling and translation files within a Symfony bundl...
Architecture fit
Bundle subclass, a DI extension, and service definitions bound to symfony/http-kernel, symfony/translation, symfony/console, and symfony/finder. It assumes a kernel/container boot lifecycle that Laravel does not have.Illuminate\Translation\Translator + FileLoader) does not consume symfony/translation. There is no adapter seam — no interface the bundle can implement to slot in behind __(), trans(), or trans_choice()..po/.mo libraries directly. The bundle's value — the Symfony wiring — is exactly the part Laravel cannot use.Integration feasibility
composer require, enable the bundle, point the translator at the gettext loader, add msgfmt to the build..po → lang/{locale}/*.php (or lang/{locale}.json) conversion step, or a thin custom FileLoader reading compiled .mo via a pure-PHP gettext library (gettext/gettext, oscarotero/php-gettext). Either is ~1–2 weeks including tests.ext-gettext present, msgfmt in the build image, and target locales generated on the host (locale -a). These are the recurring failure points in containerized PHP deploys and are the most common reason "it worked locally."Technical risk
setlocale, putenv, textdomain). It is safe under PHP-FPM (one locale per process/request) and unsafe under Octane, Swoole, RoadRunner, or long-lived queue workers, where locale bleeds across requests and jobs. If Octane is on the roadmap — and in Laravel it usually is — this is a blocking constraint, not a caveat..mo falls back to the msgid rather than erroring. That is a quiet quality regression that ships to production and surfaces as a customer complaint, not a build failure.opportunity score of 4.52 against 0 stars and 0 dependents is inconsistent with observable adoption. Weight hard signals (stars, dependents, commit recency, test presence) over the composite.Key questions
composer.json declare, and does the code use APIs removed after Symfony 4?ext-gettext at runtime or only at compile time? Is there a non-gettext fallback loader?Stack fit
.po as the canonical translation source, add a build step that compiles .mo and converts to Laravel-native lang/*.php or lang/*.json. This preserves agency tooling compatibility while using Laravel's native, worker-safe translator. The bundle serves only as a reference implementation..po catalogues: strong fit. Slots into the existing translator pipeline with minimal code change.Migration path
.po files as the canonical artifact and treat whichever runtime loader you pick as a consumer — never the source of truth. This is the single decision that keeps exit costs low regardless of which loader wins.de), behind config. No user-visible change.msgfmt --check plus a diff check that every locale's .po has a valid UTF-8 header and correct Plural-Forms. Fail the build on missing or empty catalogues — this is the control that converts silent fallback into a loud failure.For Laravel specifically, Phases 1–4 collapse into Phase 2 plus a converter — there is no runtime dependency to add or remove.
Compatibility
ext-gettext and intl availability across dev, CI, and production images. CI/prod drift is the classic source of "works locally, English in prod."Plural-Forms headers — plural-rule errors are subtle and locale-specific (Polish, Russian, Czech, Arabic, Welsh are the ones that expose ad-hoc key schemes).setlocale/putenv state — otherwise the Octane problem follows you.Sequencing
.po, assert translated output, plural forms, msgctxt, and fallback behavior.msgfmt build step — not a laptop.Maintenance
"x.y.*" or a commit SHA), add a Dependabot/Renovate watch with a named owner, and record why the code is vendored and what would make us remove it. Undocumented vendored code becomes permanent by default.Support
.mo lives, how to add a locale, how fallback behaves, who reviews .po diffs..mo, missing system locale on the host, stale build artifact.Scaling
.mo files are memory-mapped, lookups are fast, and memory per process is modest. This is a genuine performance advantage over large PHP-array catalogues loaded through opcache.Failure modes
.mo → silent fallback to msgid. Highest-frequency failure, lowest detectability. Mitigate with a startup or CI assertion that every enabled locale has a non-empty, freshly built catalogue.setlocale() succeeds.Plural-Forms header → grammatically wrong output in plural-heavy locales, invisible to English-speaking reviewers.msgctxt collisions with untagged keys, and stale gettext in-process caches requiring a process restart (or cache clear) after deploys — a deploy-time foot-gun worth documenting..po headers) → mojibake in the UI.Ramp-up
xgettext, msgmerge, msgfmt, .po header semantics — which is roughly a week to fluency and is not documented by this package..po and Poedit, which is an advantage; internal staff reviewing .po diffs in pull requests is a new workflow that needs a short guide.How can I help you explore Laravel packages today?