Weave Code
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

niels-numbers/laravel-localizer

Locale-aware routing for Laravel with static, route:cache-ready localized routes. Auto-detects language, redirects to prefixed URLs, and resolves route() to the correct locale. Successor to mcamara/laravel-localization.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require niels-numbers/laravel-localizer
    

    Publish the config (optional):

    php artisan vendor:publish --provider="NielsNumbers\Localizer\LocalizerServiceProvider" --tag="config"
    
  2. Configure Locales: Edit config/localizer.php to define supported locales (e.g., ['en', 'de', 'fr']) and set a default.

  3. First Route:

    Route::localize(function () {
        Route::get('/about', [AboutController::class, 'index'])->name('about');
    });
    

    This generates:

    • /about (auto-detects locale)
    • /de/about, /fr/about (explicit locales)
  4. Middleware: Add to app/Http/Kernel.php:

    protected $middlewareGroups = [
        'web' => [
            // ... other middleware
            \NielsNumbers\Localizer\Middleware\SetLocale::class,
            \NielsNumbers\Localizer\Middleware\RedirectLocale::class,
        ],
    ];
    
  5. Test:

    • Visit /about → Redirects to /en/about (or detected locale).
    • Visit /de/about → Stays on /de/about.

First Use Case: Language Switcher

Add a Blade snippet to switch locales:

@foreach(config('localizer.locales') as $locale)
    <a href="{{ route('about', [], ['locale' => $locale]) }}">
        {{ $locale }}
    </a>
@endforeach

Or use the built-in helper:

<a href="{{ route('about')->localizedSwitcherUrl('de') }}">Deutsch</a>

Implementation Patterns

Core Workflow: Route Registration

  1. Localized Routes with Naming Enforcement:

    Route::localize(function () {
        Route::get('/about', [AboutController::class, 'index'])->name('about'); // Named route
        Route::get('/contact', [ContactController::class, 'index']); // Unnamed route (now stays unnamed)
    });
    
    • Key Change: Unnamed routes inside Route::localize() no longer inherit a default name (with_locale. prefix). They remain unnamed but are still localized.
    • New Behavior: Route::translate() now requires all routes to be named. If a route lacks a name, it throws UnnamedTranslatedRouteException at registration.
  2. Mixed Routes: Combine with standard routes:

    Route::get('/static-page', [StaticController::class, 'index'])->name('static');
    Route::localize(function () {
        Route::get('/dynamic-page', [DynamicController::class, 'index'])->name('dynamic');
    });
    

Integration with Frontend Frameworks

Ziggy (Laravel 10+)

  1. Bind the generator in AppServiceProvider:
    public function register()
    {
        $this->app->bind(
            \Tighten\Ziggy\BladeRouteGenerator::class,
            \NielsNumbers\Localizer\Ziggy\LocalizerBladeRouteGeneratorV2::class
        );
    }
    
  2. Use in Blade:
    @routes
    <script>
        // Ziggy's `route()` helper now respects locales
        const url = route('about'); // Resolves to `/en/about` or current locale
    </script>
    

Inertia.js

  1. Share the current locale in app/Http/Middleware/HandleInertiaRequests.php:
    public function share(Request $request): array
    {
        return array_merge(parent::share($request), [
            'locale' => app(\NielsNumbers\Localizer\Facades\Localizer::class)->getCurrentLocale(),
        ]);
    }
    
  2. Use in Vue/React:
    // Vue example
    const { locale } = window.__INERTIA__.shared;
    

Advanced Patterns

Dynamic Locale Detection

Extend the detector chain in config/localizer.php:

'detectors' => [
    \NielsNumbers\Localizer\Detectors\AcceptLanguageDetector::class,
    \NielsNumbers\Localizer\Detectors\UserDetector::class,
    \NielsNumbers\Localizer\Detectors\SessionDetector::class,
    \App\Detectors\CustomDetector::class, // Your custom logic
],

Locale-Aware Redirects

Use Redirect::localized():

return Redirect::localized()->route('dashboard');

Middleware for Locale-Specific Logic

public function handle(Request $request, Closure $next)
{
    $locale = app(\NielsNumbers\Localizer\Facades\Localizer::class)->getCurrentLocale();
    if ($locale === 'de') {
        // German-specific logic
    }
    return $next($request);
}

Gotchas and Tips

Pitfalls

  1. Unnamed Routes in Route::localize():

    • New Behavior: Unnamed routes inside Route::localize() now stay unnamed (previously inherited a default name like with_locale.).
    • Impact: These routes cannot be language-switched via localizedSwitcherUrl() or route()->localizedSwitcherUrl().
    • Fix: Explicitly name routes if you need locale switching:
      Route::localize(function () {
          Route::get('/contact', [ContactController::class, 'index'])->name('contact');
      });
      
  2. Route::translate() Naming Requirement:

    • New Behavior: Route::translate() now throws UnnamedTranslatedRouteException if any route inside it lacks a name.
    • Impact: Fails fast at registration (previously failed at runtime during URL resolution).
    • Fix: Ensure all routes inside Route::translate() are named:
      Route::translate(['en' => 'about', 'de' => 'ueber'], function () {
          Route::get('/', [HomeController::class, 'index'])->name('home'); // Named
      });
      
  3. Case-Sensitive URLs:

    • /EN/about (wrong case) will 404 unless hide_default_locale is true.
    • Fix: Use Route::localizedUrl() helpers or enforce lowercase in middleware.
  4. Route Caching:

    • After php artisan route:cache, clear it if locales or routes are modified:
      php artisan route:clear
      
  5. Middleware Order:

    • SetLocale must run after StartSession and before SubstituteBindings.
    • Fix: Use web(append: [SetLocale, RedirectLocale]) in Kernel.php.
  6. Query String Loss:

    • Switching locales may drop query strings (e.g., /posts?page=2/de/posts).
    • Fix: Use localizedSwitcherUrl() with preserved query params:
      {{ route('posts.index')->localizedSwitcherUrl('de', [], true) }}
      
  7. Ziggy Version Mismatch:

    • If using Ziggy v2, bind LocalizerBladeRouteGeneratorV2 (not V1).
    • Fix: Check composer.json for Ziggy version and update bindings.

Debugging Tips

  1. Check Current Locale:

    dd(app(\NielsNumbers\Localizer\Facades\Localizer::class)->getCurrentLocale());
    
  2. Inspect Route Names:

    • with_locale.about vs. without_locale.about:
      dd(Route::current()->getName());
      
  3. Disable Redirects Temporarily: Set hide_default_locale: true in config/localizer.php to test unprefixed URLs.

  4. Log Detector Chain: Add a custom detector to log the resolution process:

    class DebugDetector implements DetectorInterface {
        public function detect(Request $request): ?string {
            \Log::info('Locale detection:', [
                'accept_language' => $request->header('Accept-Language'),
                'session' => $request->session()->get('locale'),
            ]);
            return null; // Let other detectors handle it
        }
    }
    
  5. Catch UnnamedTranslatedRouteException: Wrap Route::translate() in a try-catch to handle missing names gracefully:

    try {
        Route::translate(['en' => 'home', 'de' => 'start'], function () {
            Route::get('/', [HomeController::class, 'index']); // Unnamed (will throw)
        });
    } catch (\NielsNumbers\Localizer\Exceptions\UnnamedTranslatedRouteException $e) {
        \Log::error('Missing route name in translated group: ' . $e->getMessage());
    }
    

Extension Points

  1. Custom Detectors: Implement DetectorInterface:
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.
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony
spatie/flare-daemon-runtime