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 Localizer Laravel Package

devwizardhq/laravel-localizer

Seamlessly bridge Laravel translations to React/Vue SPAs: auto-scan translation keys, auto-translate missing strings with Google Translate, and generate type-safe TypeScript files with caching, vendor support, RTL, and framework-agnostic output.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and run the wizard. The package ships an interactive installer that does most of the wiring for you:

composer require devwizardhq/laravel-localizer
php artisan localizer:install

It publishes config/localizer.php, creates default locale files, publishes + registers App\Http\Middleware\LocalizerMiddleware, sets up the TypeScript output directory, and runs an initial generation pass. Prefer manual steps? vendor:publish --tag="laravel-localizer-config" and --tag="laravel-localizer-middleware", then append the middleware in bootstrap/app.php under ->withMiddleware(...)->web(append: [...]).

2. Look here first, in this order. config/localizer.php (locales, paths, scan rules), the published app/Http/Middleware/LocalizerMiddleware.php (detection chain you'll likely customize), lang/ (source of truth), and resources/js/lang/ (generated build artifacts — never hand-edit).

3. Declare your locales. Add entries to available; each is ['label' => ..., 'flag' => ..., 'dir' => 'ltr'|'rtl']. Keep default and fallback inside available.

4. Sync, generate, consume.

php artisan localizer:sync --all
php artisan localizer:generate --all
npm install @devwizard/laravel-localizer-react   # or -vue

5. First real use case. Add __('welcome') in a controller or Blade view, run localizer:sync --all, then localizer:generate --all. You now get a typed en.ts you can import in React/Vue, plus locale.current, locale.dir, and locale.available shared automatically via Inertia — enough to render a working language switcher on day one.

Implementation Patterns

Author server-side, treat TypeScript as a build artifact. Translations live in lang/*.json and lang/{locale}/*.php; resources/js/lang/*.ts is derived. The installer gitignores it, so the correct mental model is "compile translations" — regenerate in CI before npm run build, not during feature work.

Wire the deploy pipeline explicitly. Order matters:

php artisan localizer:generate --all
npm run build
php artisan config:cache

Missing the generate step is the most common production break: imports resolve locally but fail after a clean clone.

Use the detection chain instead of inventing one. Priority is ?locale=fr → X-Locale header → session → $user->getLocale() → Accept-Language → default. For a persistent switcher, implement getLocale() on your User model and let the session layer handle guests. For shareable links (/page?locale=ar), the query param already wins.

Drive the switcher from shared Inertia props. locale.available gives you label, flag, and direction without a second endpoint. Apply locale.dir to <html dir="..."> yourself — the package supplies the value, not the DOM attribute.

Manage translations programmatically for admin UIs. The Localizer facade covers CRUD without touching files directly:

use DevWizard\Localizer\Facades\Localizer;

Localizer::create('fr', fromLocale: 'en');   // scaffold a locale
Localizer::bulkSet($request->validated(), 'fr');
Localizer::set('welcome', 'Bienvenue', 'fr');        // JSON file
Localizer::setPhp('validation.required', '...', 'fr'); // PHP file
Localizer::rename('en', 'en-US');
Localizer::delete('fr');

Use set/bulkSet for flat UI strings and setPhp for dotted validation/auth namespaces — see the gotcha below.

Handle the key-shape decision up front. Dotless keys land in {locale}.json; dotted keys land in {locale}/{file}.php. Decide this convention for your team before you accumulate hundreds of keys, because moving between the two later is a manual file migration.

Let vendor translations flow through. The generator already folds package translations in. Don't duplicate Laravel's validation.php into your app files — regenerate and they appear, which keeps framework upgrades honest.

Schedule or queue the heavier commands. localizer:sync is cheap and safe to run in CI. localizer:translate hits an external service — run it as a queued job or a scheduled command with --source/--target, not in a request or deploy hook. It skips already-translated keys, so re-running is idempotent and a good top-up strategy after adding keys.

Keep Blade and SPA paths on the same source. Blade can keep using __() normally — the scanner sees those calls — so a hybrid app doesn't need two translation stores. Only the generated TypeScript differs.

Gotchas and Tips

Dynamic keys are invisible to the scanner. __('validation.' . $rule) or __($label) won't be discovered; the scanner parses __(), trans(), and lang() calls with extractable string literals. Audit for interpolation and add those keys manually, ideally via Localizer::setPhp() so they live in a predictable file.

Frontend-only keys are invisible too. The scanner looks for PHP helpers, not t('...') in React/Vue. If the SPA needs a string the backend never translates, seed it through the facade or a seeder — otherwise the key silently never appears in en.ts.

A period in a JSON key routes it to a PHP file. __('Version 1.2 notes') becomes lang/en/Version 1.php-ish behavior, surprising everyone. Avoid dots in human-readable UI strings, or intentionally namespace everything.

set vs setPhp is not interchangeable. They target different file types, and get() merges both — so the same key in both places can shadow unexpectedly. Pick one home per key.

In-memory caching means long-lived processes go stale. Facade writes invalidate the request's cache, but Octane workers or queue jobs that already booted may hold old data. If you mutate translations at runtime, restart workers or re-read explicitly.

Middleware placement is load-bearing. It needs the session and Inertia middleware to do anything useful. Appending it to the web group (as documented) is correct; registering it globally on API-only routes gets you nothing but wasted cycles.

Don't skip the session or the user method and expect persistence. Without getLocale() or a session write, every request re-derives from Accept-Language, so a switcher appears to "forget" on the next navigation.

Locale codes must match everywhere. available keys, lang/ directory names, $user->getLocale() return values, and any --locales= flags. rename('en', 'en-US') is convenient but breaks stored user preferences — plan a migration.

as const types only cover generated keys. Full IDE support is real, but a key you add at runtime won't typecheck until the next generate. That's the intended trade-off, not a bug.

Customize detection by editing the published middleware, not by forking. It's published into App\Http\Middleware precisely so you can insert a tenant/domain-based resolution step into the chain. Similarly, scan.include/scan.exclude/extensions in config handle monorepos and Laravel Modules layouts without code changes.

Auto-translate needs an extra dependency and a tolerance for its output. composer require stichoza/google-translate-php is required, and the underlying service is an unofficial Google endpoint — treat machine output as a first draft, expect rate limiting on large key sets, and never let it overwrite reviewed copy (it skips existing keys, which is your safety net).

Assess maturity before betting on it. Seven stars, no dependents, and a 2026 release date: budget time to read the command and middleware source, pin the version in composer.json, and wrap facade calls behind your own service class so swapping the package later is a one-file change.

Debugging checklist when a key "doesn't work": confirm the key exists (Localizer::has($key, 'en')), inspect the actual file the sync wrote (JSON vs lang/en/*.php), check resources/js/lang/index.ts exports your locale, and verify the middleware ran (dump the shared locale prop in an Inertia response). Nine times out of ten it's the dot-notation file split or a forgotten localizer:generate.

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.
tobya/saloonfire
kjmtrue/laravel-debugbar
hexincorp/debugbar
razorbacks/laravel-shibboleth
adilazhari/laravel-idempotency
mamitech/laravel-slow-query-log
mamitech/laravel-http-logger
medeirosrafael/docxmustache
webdevlabs/phreak
vis/log-envelope
digitickets/lalit
acacha/names
scool/foundation
frankkessler/guzzle-oauth2-middleware
delatbabel/site-config
delatbabel/applog
eusonlito/laravel-gettext
henzeb/housepedia-intelligence-laravel
aliqsm/laravel-authkit
itsjustvita/laravel-wp-migrator