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

Browser Locale Laravel Package

codezero/browser-locale

Parse the browser’s Accept-Language header to get a visitor’s preferred locales in order. Returns primary locale or full locale list with language/region parts. Works in vanilla PHP and integrates with Laravel via the container.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require codezero/browser-locale
    

    Laravel 5.5+ auto-registers the service provider; no manual configuration needed.

  2. First Use Case: Retrieve the primary locale in a Laravel controller or middleware:

    use CodeZero\BrowserLocale\BrowserLocale;
    
    $browserLocale = app(BrowserLocale::class);
    $locale = $browserLocale->getLocale();
    
    if ($locale) {
        app()->setLocale($locale->language); // Set Laravel's locale
        // Or use $locale->locale (e.g., "en-US") for granular logic
    }
    
  3. Where to Look First:

    • getLocale(): For the highest-priority locale (e.g., en-US).
    • getLocales(): For all locales sorted by preference weight.
    • Filters: Use LocaleFilter, LanguageFilter, etc., for specific data extraction (e.g., just languages or countries).

Implementation Patterns

Core Workflows

1. Middleware for Locale Detection

Create a middleware to set the app locale dynamically:

namespace App\Http\Middleware;

use Closure;
use CodeZero\BrowserLocale\BrowserLocale;

class SetLocaleMiddleware
{
    public function handle($request, Closure $next)
    {
        $browserLocale = app(BrowserLocale::class);
        $locale = $browserLocale->getLocale();

        if ($locale) {
            app()->setLocale($locale->language);
        }

        return $next($request);
    }
}

Register in app/Http/Kernel.php:

protected $middleware = [
    \App\Http\Middleware\SetLocaleMiddleware::class,
];

2. Fallback Logic

Use getLocales() to implement fallback chains (e.g., en-USenen-GB):

$locales = $browserLocale->getLocales();
$supportedLocales = ['en-US', 'en', 'fr', 'de'];

foreach ($locales as $locale) {
    if (in_array($locale->locale, $supportedLocales)) {
        app()->setLocale($locale->language);
        break;
    }
}

3. Filtering for Analytics or UI

Extract specific data (e.g., languages or countries) for reporting or conditional rendering:

$languageFilter = new \CodeZero\BrowserLocale\Filters\LanguageFilter();
$languages = $browserLocale->filter($languageFilter);

// Example: Show a "Welcome back" message in the user's language
if (in_array('nl', $languages)) {
    return view('welcome_nl');
}

4. Custom Filters

Implement a custom filter for niche use cases (e.g., extract regions only for EU compliance):

use CodeZero\BrowserLocale\Filters\Filter;

class RegionFilter implements Filter
{
    public function filter(array $locales): array
    {
        return array_unique(array_filter($locales, function ($locale) {
            return in_array($locale->country, ['DE', 'FR', 'IT']);
        }));
    }
}

Usage:

$regionFilter = new RegionFilter();
$regions = $browserLocale->filter($regionFilter);

Integration Tips

Laravel-Specific

  • Service Container: Prefer app(BrowserLocale::class) over new BrowserLocale() for testability and dependency injection.
  • Caching: Cache the parsed locales in middleware or a service to avoid reprocessing per request:
    $cacheKey = 'browser_locale_' . md5($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? '');
    $locales = cache()->remember($cacheKey, now()->addHours(1), function () use ($browserLocale) {
        return $browserLocale->getLocales();
    });
    
  • Testing: Mock BrowserLocale in tests:
    $this->app->instance(BrowserLocale::class, Mockery::mock(BrowserLocale::class));
    

Vanilla PHP

  • Manual Instantiation: Pass $_SERVER["HTTP_ACCEPT_LANGUAGE"] directly:
    $browserLocale = new BrowserLocale($_SERVER["HTTP_ACCEPT_LANGUAGE"]);
    
  • Error Handling: Check for null returns (e.g., if the header is missing or malformed).

Edge Cases

  • Malformed Headers: The package handles invalid formats gracefully (returns null or empty arrays).
  • No Locale: Always check for null when using getLocale():
    $locale = $browserLocale->getLocale();
    if (!$locale) {
        // Fallback to default or redirect to language selector
    }
    

Gotchas and Tips

Pitfalls

  1. Locale Weight Misinterpretation:

    • The weight property (e.g., 0.8) indicates preference strength, not support status. A weight of 0.4 doesn’t mean the locale is invalid—it’s just less preferred.
    • Fix: Use getLocales() to iterate through all locales in order of preference.
  2. Duplicate Locales:

    • The input string may contain duplicates (e.g., en-US,en). The package deduplicates results automatically, but be aware of the original order.
    • Tip: Use getLocales() to see the full sorted list, including weights.
  3. Country-Only Locales:

    • Some locales are language-only (e.g., en). The CountryFilter will skip these, which may be unexpected.
    • Workaround: Combine with LanguageFilter or implement a custom filter.
  4. Laravel Locale Conflicts:

    • If you set app()->setLocale($locale->language), ensure the locale exists in your config/app.php under supportedLocales. Otherwise, Laravel may fall back to the default.
    • Tip: Validate supported locales before setting:
      $supported = config('app.supportedLocales', []);
      if (in_array($locale->language, $supported)) {
          app()->setLocale($locale->language);
      }
      
  5. Middleware Order:

    • Place locale-setting middleware early in the stack (e.g., before auth or localization middleware) to ensure it runs on all requests.

Debugging Tips

  1. Inspect Raw Input: Dump $_SERVER["HTTP_ACCEPT_LANGUAGE"] to verify the input string:

    dd($_SERVER["HTTP_ACCEPT_LANGUAGE"] ?? 'Header missing');
    

    Example output: en-US,en;q=0.8,fr;q=0.6.

  2. Check Filter Outputs: Use filters to debug what’s being parsed:

    $languageFilter = new \CodeZero\BrowserLocale\Filters\LanguageFilter();
    $countriesFilter = new \CodeZero\BrowserLocale\Filters\CountryFilter();
    dd($browserLocale->filter($languageFilter), $browserLocale->filter($countriesFilter));
    
  3. Test Edge Cases: Manually test with malformed strings:

    $browserLocale = new BrowserLocale('en-US,,fr;q=invalid,nl');
    dd($browserLocale->getLocales());
    

    Expected: The package ignores invalid q values and empty entries.

  4. Laravel Logs: Enable debug logging to trace middleware execution:

    \Log::debug('Browser locale:', [
        'raw' => $_SERVER["HTTP_ACCEPT_LANGUAGE"] ?? null,
        'parsed' => $browserLocale->getLocales(),
    ]);
    

Extension Points

  1. Custom Parsing Logic: Extend the BrowserLocale class to add pre-processing (e.g., normalize the input string):

    class ExtendedBrowserLocale extends BrowserLocale
    {
        public function __construct($acceptLanguage)
        {
            // Pre-process: replace 'und' with 'en' for testing
            $acceptLanguage = str_replace('und', 'en', $acceptLanguage);
            parent::__construct($acceptLanguage);
        }
    }
    
  2. Database Integration: Store parsed locales in the database for analytics:

    $locales = $browserLocale->getLocales();
    foreach ($locales as $locale) {
        UserLocale::updateOrCreate(
            ['user_id' => auth()->id()],
            ['locale' => $locale->locale, 'weight' => $locale->weight]
        );
    }
    
  3. API Headers: Use the package to set Accept-Language headers in API responses:

    $response->header('Accept-Language', implode(',', array_map(
        fn($locale) => $locale->locale,
        $browserLocale->getLocales()
    )));
    
  4. Geolocation Fallback: Combine with a geolocation service for fallback logic:

    $browserLocales = $browserLocale->getLocales();
    $
    
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.
besmartand-pro/php-quality-config
sentix/ai-chatbot
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity