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

Laravel Gettext Laravel Package

zerospam/laravel-gettext

Gettext-based localization for Laravel: load PO/MO translation catalogs, translate strings with plural forms and contexts, switch locales at runtime, and use translations in Blade views. A practical alternative to Laravel’s default PHP/JSON language files.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and publish its config:

composer require zerospam/laravel-gettext
php artisan vendor:publish --provider="Xavrsl\LaravelGettext\LaravelGettextServiceProvider" --tag=config

Open config/gettext.php first. The settings that matter day-to-day are:

'locale'          => 'en_US',
'fallback-locale' => 'en_US',
'locale-dir'      => storage_path('gettext'),
'domain'          => 'messages',
'source-paths'    => [base_path('app'), base_path('resources/views')],
'encoding'        => 'UTF-8',

Make sure the runtime has the PHP gettext extension enabled, and the build/dev environment has GNU gettext binaries (xgettext, msgmerge, msgfmt). The binaries are only needed for extraction/compilation; production only needs the PHP extension and the compiled .mo files.

A first translation loop:

  1. Mark one string in a Blade view:

    <h1>@gettext('Welcome to the dashboard')</h1>
    

    In PHP use the package’s gettext helpers (_('...') / gettext('...')) rather than Laravel’s __() unless your version wires __() to gettext.

  2. Generate/update the catalog:

    php artisan gettext:update
    
  3. Open storage/gettext/fr_FR/LC_MESSAGES/messages.po in PoEdit, translate, save (PoEdit compiles the .mo), or run:

    php artisan gettext:compile
    
  4. Set the locale via the package middleware or directly:

    LaravelGettext::setLocale('fr_FR');
    

Where to look when something is wrong: the .po/.mo path under locale-dir, the domain in config, and whether setlocale() actually succeeded on the host.

Implementation Patterns

Locale negotiation. Most applications use a URL prefix plus the package middleware. Register the middleware alias and group routes:

Route::prefix('{locale}')
    ->middleware('gettext')
    ->group(function () {
        Route::get('/', [HomeController::class, 'index']);
    });

The middleware reads the locale segment (or session) and calls LaravelGettext::setLocale(). Always validate the incoming locale before setting it:

if (LaravelGettext::isLocaleAvailable($locale)) {
    LaravelGettext::setLocale($locale);
}

A language switcher is usually a small POST route that stores the chosen locale in the session and redirects back.

String marking. Use one convention consistently:

  • Blade: @gettext('Save changes')
  • PHP: _('Save changes') or gettext('Save changes')
  • Plurals: use the gettext plural helper (ngettext()) rather than Laravel’s trans_choice(), unless your version explicitly bridges them.

Add resources/views and any package/module source directories to source-paths; otherwise new strings are invisible to gettext:update.

Daily translation workflow. The loop is: add/change strings → php artisan gettext:update → translate in PoEdit → php artisan gettext:compile → verify in the browser. Treat .po files as source and commit them. Decide explicitly whether to commit .mo files: committing them makes deploys simpler and avoids requiring msgfmt on production, but creates noisy binary diffs.

Deployment. If you compile on deploy, install gettext in the image and run php artisan gettext:compile after code is in place. If you commit .mo, ensure the deploy pipeline does not skip them or overwrite them with stale artifacts. For Docker, install both gettext and locales, generate the locales you support, and set a UTF-8 LANG.

Coexistence with Laravel translations. Keep Laravel’s lang/ files for framework strings (validation, auth, pagination) and gettext for application content, or use gettext everywhere consistently. Mixing __() and @gettext() for the same key creates two translation stores and confusing “missing translation” reports.

Testing. Set the locale explicitly in the test setup:

LaravelGettext::setLocale('fr_FR');
$this->assertSame('Bonjour', _('Hello'));

Also assert that middleware falls back safely for unsupported locales. Keep a small smoke test that verifies a compiled .mo exists for every shipped locale.

Extension points. Use a custom middleware or service if locale needs to come from a tenant, cookie, or user profile. Publish and override the config to move locale-dir into the repo (for example resources/lang/gettext) if you want translators working directly in version control.

Gotchas and Tips

  • Missing system locales. setlocale(LC_ALL, 'fr_FR.UTF-8') returns false if the locale is not installed, and gettext silently returns the original string. Check locale -a | grep fr_FR on the server and generate locales in the Docker image (locale-gen fr_FR.UTF-8).
  • Locale name variants. en_US, en_US.UTF-8, and en_US.utf8 are not interchangeable across systems. Normalize the config to the exact name reported by locale -a.
  • Forgotten compilation. The most common “translation not showing” cause is an outdated or missing .mo. PoEdit compiles on save; the artisan workflow does not. Compare .po and .mo mtimes and run gettext:compile after every catalog edit.
  • Long-running workers. PHP gettext caches catalogs per process. Under Octane, Swoole, or queue workers, changing locale per request can leak between requests. Rebind locale carefully on each request (putenv, setlocale, textdomain) and test under the same runtime you deploy.
  • Extraction limitations. xgettext only extracts literal strings. Dynamic calls like _($label) are skipped; add an explicit literal or a translator comment. Verify source-paths includes every directory that contains translatable strings.
  • Permission and path errors. gettext:update writes under locale-dir. In container builds this may be read-only or owned by another user. Compile during build or ensure the directory is writable.
  • Encoding problems. Set UTF-8 in both config/gettext.php and the .po header. A BOM or Windows-1252 editor setting produces mojibake in the UI even though the catalog compiles.
  • Plural forms. The Plural-Forms header in each .po must match the language. Copying a .po template between languages and translating only the strings often leaves wrong plural rules.
  • URL input is untrusted. Never pass a raw route segment to setLocale() without checking isLocaleAvailable(). Otherwise you can create traversal-like paths or trigger errors.
  • Config cache. After editing config/gettext.php, run php artisan config:clear (and config:cache again in production) before debugging further.
  • Debugging checklist. Confirm bindtextdomain() points at the expected directory, textdomain() returns the configured domain, setlocale() returns a non-false value, and msgunfmt messages.mo contains the expected entry. A one-line tinker call to gettext('...') isolates runtime issues from Blade issues.
  • Platform support. Windows development machines usually lack gettext binaries; use WSL/Docker, or commit compiled .mo files so local runtime does not need msgfmt.
  • Package maturity. This is a small, older package. Pin the version in composer.json, verify compatibility with your Laravel/PHP version, and keep a fallback plan (Laravel’s native lang/ or a maintained gettext wrapper) if you depend on long-term support.
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.
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
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect