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

Language Detector Laravel Package

vluzrmos/language-detector

Laravel package for detecting the language of text strings through a simple API. It includes a service provider, facade, and configuration, making it easy to identify languages for localization, validation, or routing decisions with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish the config

composer require vluzrmos/language-detector
php artisan vendor:publish --provider="Vluzrmos\LanguageDetector\LanguageDetectorServiceProvider"

Laravel's package auto-discovery registers the service provider and the LanguageDetector facade, so there is no manual config/app.php wiring on Laravel 5.5+.

Read the config file first. Everything in this package is driven by config/language-detector.php: the ordered list of detectors (browser Accept-Language, subdomain, route prefix), the set of supported languages, and the fallback locale. The exact key names (detectors vs providers) have shifted between majors — open the published file before writing code against it, and treat that file as the real API surface. The second place to look is the Detectors/ directory in vendor/, since each detector is a small class you can read in a minute and copy as a template.

First use case — negotiate the locale once per request. The package's core value is turning an ambiguous request into a single locale string. Resolve it in a middleware and set it on the app:

// app/Http/Middleware/SetLocale.php
public function handle($request, Closure $next)
{
    $locale = LanguageDetector::detect();   // e.g. "pt_BR" or "pt-br"
    app()->setLocale($locale);
    Carbon::setLocale($locale);

    return $next($request);
}

Register it globally (or in the web group) and you are done. Everything else — route prefixes, subdomains, persistence — is configuration on top of this same call.

Implementation Patterns

Detector chain, ordered by trust. The idiomatic setup is to put explicit signals before implicit ones: route prefix → subdomain → session/cookie → browser header → fallback. A user who typed /pt/painel in the URL should never be overridden by a stale Accept-Language: en-US. Configure the order once and let LanguageDetector::detect() walk it.

Route-prefix routing. The most common production pattern is a locale segment with an optional prefix, so /painel and /pt/painel both resolve:

Route::group([
    'prefix' => '{locale?}',
    'middleware' => SetLocale::class,
], function () {
    Route::get('/', HomeController::class);
});

Constrain the parameter to your supported list (->whereIn('locale', config('language-detector.supported'))) so /login is not mistaken for a login locale. Pair this with a redirector that sends / to the detected locale's URL.

Subdomain routing for market segmentation. pt.example.com / en.example.com is the cleanest signal when you have genuinely separate regional sites, because it survives deep links and is shareable. Combine it with a canonical redirect so www and the bare domain land on a known locale.

Persist an explicit choice. When a user picks a language from a switcher, store it (session or a long-lived cookie) and put that detector ahead of the browser header. Without this the switcher appears broken on the next request. Then redirect back to the same URL with the new prefix.

Normalize, don't trust. Detectors return BCP-47-ish codes (pt-BR, en-GB). Laravel's translator wants a key that exists in resources/lang (pt_BR, pt, en). Add a small normalizer that lowercases, tries exact match, then the base language, then the fallback:

protected function normalize(string $detected): string
{
    $supported = config('language-detector.supported');

    foreach ([$detected, str_replace('-', '_', $detected), explode('-', $detected)[0]] as $candidate) {
        if (in_array($candidate, $supported, true)) {
            return $candidate;
        }
    }

    return config('app.fallback_locale');
}

Compose with Laravel-localization-style URL generation. If you also use mcamara/laravel-localization, keep exactly one of the two owning the {locale} parameter and the locale setter. Running both produces duplicated prefixes and double redirects.

Test it with headers, not mocks. Feature tests are far more valuable than unit tests here:

$this->withHeader('Accept-Language', 'pt-BR,pt;q=0.9,en;q=0.8')
     ->get('/')
     ->assertSee('Olá');

Assert on rendered content or on app()->getLocale(), not on the detector object.

Gotchas and Tips

Detector order is the whole product. Most "it doesn't work" reports are really "a lower-priority detector won." Log the detected value in the middleware (Log::debug('locale', ['detected' => $locale])) while wiring it up, then remove the log.

Accept-Language parsing is approximate. Q-values, wildcards (*), and three-letter codes are handled inconsistently across versions. For anything user-facing, treat the browser detector as a hint and always run its output through the normalizer above. Never let an unmapped code reach setLocale() — an unknown locale silently renders raw translation keys.

Route prefix detector will swallow real routes. /api, /admin, /static look exactly like locale segments. Always constrain the parameter, or put the locale group after your non-localized groups and exclude known prefixes explicitly.

Region fallback is not automatic. pt-BR will not fall back to a pt translation directory on its own in older Laravel versions; Laravel's translator does have a fallback chain but only if fallback_locale and the directory names are set consistently. Decide on one convention (pt_BR directories) and enforce it in the normalizer.

Middleware position matters. If you persist the choice in the session, the locale middleware must run after StartSession. If any middleware renders errors or emails with translated strings, it must run after yours. Global middleware registered in Kernel::$middleware sits before the web group — usually what you want for the locale setter, but verify with php artisan route:list -v.

Config cache. After editing language-detector.php, run php artisan config:clear in dev and make sure your deploy runs config:cache. Detector class names resolved from config will fatal if the class was renamed in a major upgrade but the cached config is stale.

Singleton state across tests. If the detector caches its result per container lifecycle, consecutive requests inside one test method can return the first request's locale. Reset between assertions or use separate tests per locale.

Subdomain detection depends on the request host. Behind a proxy or with a mismatched APP_URL, $request->getHost() may be localhost, making subdomain detection fall through to the browser. Configure TrustProxies before blaming the package.

Extension point: write your own detector. The cleanest customization is a small class implementing the package's detector contract — a GeoIpDetector, a UserPreferenceDetector reading $user->locale, or a QueryStringDetector for ?lang=. Register it in the config array; no forking required. This is also the right answer when the built-in browser parser is not good enough for your needs.

Scope check. This package negotiates a locale; it does not translate, route-translate, or manage hreflang. With 109 stars and no dependents, treat it as a thin, readable helper rather than load-bearing infrastructure: read the detector classes (they are short), keep a fallback in your own middleware, and you will be insulated from the next major version's config rename.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console