Skip to content
Laravel package

Laravel Localizer

Sync Laravel translation keys, generate typed TypeScript language files and share locale data with React or Vue Inertia apps.

composer require devwizardhq/laravel-localizer
View on GitHub
On this page

Overview

Laravel Localizer is the backend half of a translation workflow for Laravel apps with a JavaScript frontend. It scans your code for translation keys, keeps the language files in lang/ in sync, and exports every locale as a TypeScript file that your React or Vue app can import. A middleware picks the visitor’s locale and shares it with Inertia, so the frontend always knows the current language and text direction.

Laravel stays the single source of truth for strings. The frontend reads them through a small hook or composable from the matching npm package: Laravel Localizer React or Laravel Localizer Vue. Localizer is built by the DevWizard core team and is MIT licensed.

Features

  • Key scanning: finds __(), trans() and lang() calls across PHP, Blade, JavaScript, TypeScript and Vue files and adds the missing keys to your language files.
  • TypeScript generation: one typed file per locale plus an index with translations, getAvailableLocales(), hasLocale() and getTranslations().
  • Auto-translation of missing values with Google Translate, run as a queued job (needs an optional package).
  • LocalizerMiddleware with a fixed locale detection order and Inertia shared props.
  • Right-to-left support through a dir value per locale.
  • A Localizer facade to create, rename, delete and edit locales and keys from code.
  • Interactive installer that can also set up the npm package for your framework.
  • In-memory caching of loaded translations.

Requirements

  • PHP 8.3 or newer.
  • Laravel 11, 12 or 13.
  • Inertia.js with the Laravel adapter if you want the shared locale props (the middleware skips sharing when Inertia is absent).
  • Optional: stichoza/google-translate-php for localizer:translate.

Installation

Terminal
composer require devwizardhq/laravel-localizer

Run the interactive installer. It publishes the config, creates locale files, registers the middleware and generates the first TypeScript files:

Terminal
php artisan localizer:install

Pass --framework=react or --framework=vue to choose the frontend package without a prompt, and --force to overwrite existing files.

To set things up by hand, publish the config and the middleware stub:

Terminal
php artisan vendor:publish --tag="laravel-localizer-config"
php artisan vendor:publish --tag="laravel-localizer-middleware"

Then append the published middleware to the web group in bootstrap/app.php:

PHP
->withMiddleware(function (Middleware $middleware) {
    $middleware->web(append: [
        \App\Http\Middleware\LocalizerMiddleware::class,
    ]);
})

Finally install the frontend package: React or Vue.

Configuration

Everything lives in config/localizer.php:

Key Default Purpose
default env('APP_LOCALE', 'en') Locale used when nothing else matches
fallback env('APP_FALLBACK_LOCALE', 'en') Fallback locale value; the package code itself does not read it, so use Laravel’s own app.fallback_locale for runtime fallback
available en, ar, bn, es, fr, de Locales exposed to the frontend, each with label, flag and dir
path lang_path() Where language files are read and written
typescript_output_path resource_path('js/lang') Where generated TypeScript files go
scan.include app, resources, routes Directories scanned for translation keys
scan.exclude bootstrap, lang, public, storage, vendor, node_modules Directories skipped while scanning
scan.extensions php, blade.php, js, jsx, ts, tsx, vue File types scanned

A minimal locale list with a right-to-left language:

PHP
'available' => [
    'en' => ['label' => 'English', 'flag' => '🇬🇧', 'dir' => 'ltr'],
    'ar' => ['label' => 'Arabic', 'flag' => '🇸🇦', 'dir' => 'rtl'],
],

Usage

The normal loop has three steps. Scan for keys, optionally translate them, then generate the frontend files.

Terminal
php artisan localizer:sync --all
php artisan localizer:generate --all

Keys without a dot, such as welcome, are stored in lang/en.json. Keys with a dot, such as validation.required, are stored in lang/en/validation.php.

Each locale becomes a file like resources/js/lang/en.ts that exports a constant object and a TranslationKeys type. An index.ts exports every locale and a combined translations registry.

The frontend packages do the rest: they run localizer:generate from a Vite plugin whenever a language file changes, and read the generated files through useLocalizer(). See React and Vue for the hook setup.

Locale detection

LocalizerMiddleware tries these sources in order and uses the first one that is a configured locale:

  1. The locale query parameter.
  2. The X-Locale request header.
  3. The locale stored in the session.
  4. getLocale() on the authenticated user, if the model defines it.
  5. The browser Accept-Language header.
  6. config('localizer.default').

The chosen locale is set with App::setLocale(), stored in the session and shared with Inertia as a locale prop with current, dir and available.

Deployment

Generated TypeScript files are git-ignored by the installer, so regenerate them before building the frontend:

Terminal
php artisan localizer:generate --all
pnpm build

API Reference

Artisan commands

Command Options Description
localizer:install --framework=, --force Interactive setup
localizer:sync --locales=*, --all Scan code for keys and add missing ones to language files
localizer:translate --source=, --target= Translate missing values from one locale to another
localizer:generate --locales=*, --all Write TypeScript files

Without --all or --locales, the sync and generate commands ask which locales to use.

Localizer facade

DevWizard\Localizer\Facades\Localizer proxies the Localizer class.

Method Description
create($locale, $fromLocale = null) Create a locale, optionally copying another
rename($oldLocale, $newLocale, $fromLocale = null) Rename a locale
delete($locale) Delete a locale
get($locale) All translations, JSON and PHP combined
getJson($locale) JSON translations only
getAllPhpTranslations($locale), getPhpTranslations($locale, $file) Read PHP translation files
set($key, $value = null, $locale = null) Set a JSON key
setPhp($key, $value, $locale = null) Set a dotted PHP key
bulkSet($items, $locale = null), bulkSetPhp($file, $items, $locale = null) Set many keys at once
unset($key, $locale = null), unsetPhp($key, $locale = null) Remove a key
translate($fromLocale, $toLocale) Queue an auto-translation job
availableLocales() Locales found on disk

Examples

Add a locale cloned from English, then fill in a few strings:

PHP
use DevWizard\Localizer\Facades\Localizer;

Localizer::create('fr', fromLocale: 'en');
Localizer::bulkSet(['hello' => 'Bonjour', 'goodbye' => 'Au revoir'], 'fr');
Localizer::setPhp('validation.required', 'Ce champ est obligatoire', 'fr');

Machine-translate a new locale. Install the optional package first, and run a queue worker because the translation is a queued job:

Terminal
composer require stichoza/google-translate-php
php artisan localizer:translate --source=en --target=fr

Let each user keep a preferred language by defining getLocale() on the model. The middleware uses it after the session check:

PHP
public function getLocale(): string
{
    return $this->preferred_locale;
}

FAQ & Troubleshooting

The frontend shows raw keys instead of text. Generate the files with php artisan localizer:generate --all and confirm the frontend initialises window.localizer. The setup is covered in the React and Vue docs.

The locale never changes. The locale must be listed in available in config/localizer.php. Values that are not listed are ignored by the middleware, and the detection order means an earlier source such as the query string wins over the session.

localizer:translate throws an error about GoogleTranslate. Install stichoza/google-translate-php. The translation runs as a queued job, so start a worker.

A key is missing after localizer:sync. Only literal keys in __(), trans() and lang() calls inside scan.include directories and the configured extensions are found. Keys built from variables cannot be detected.

Do I commit the generated TypeScript? The installer git-ignores them. Regenerate during deployment before the frontend build.

Changelog & Support

Release history is in the CHANGELOG and on the GitHub releases page. Open bugs and feature requests in GitHub issues. Source: DevWizardHQ/laravel-localizer. For help with multilingual products, the DevWizard team takes project contracts and monthly retainers through the contact page.