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 Domain Localization Laravel Package

kevindierkx/laravel-domain-localization

Laravel package for domain-based localization. Map locales to domains, detect the active locale from the request host, and generate localized routes and URLs, making it easy to serve each language or region from its own domain.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish the config

composer require kevindierkx/laravel-domain-localization
php artisan vendor:publish --provider="Kevindierkx\LaravelDomainLocalization\DomainLocalizationServiceProvider"

This drops a config/domain-localization.php file. That file is the first place to look — it defines the locale-to-domain map, the default locale, and the middleware/route options. Check the shipped config keys against the README before editing; this package is small enough that the config is the documentation.

Configure the domain map

The core idea is one locale per host, so example.com → en, example.fr → fr, example.nl → nl. Populate the mapping in the config file and set a sensible fallback for local development (localhost, *.test).

Register the middleware

Add the package's locale middleware to your web group in app/Http/Kernel.php (or bootstrap/app.php on Laravel 11+). It must run after TrustProxies and before anything that reads app()->getLocale(). Once it runs, App::getLocale() reflects the request host for the rest of the request lifecycle.

First use case

A marketing/landing site with ccTLDs where the same route names exist on every domain. Define routes once, group them per domain from the config map, and let the middleware resolve the locale from the host. The first thing to verify after installing: hit http://example.fr/ locally (with a hosts-file entry) and confirm app()->getLocale() returns fr.

Implementation Patterns

Drive route registration from config

Rather than hardcoding domains in routes/web.php, loop over the config map. This keeps adding a locale to a one-line config change:

foreach (config('domain-localization.domains') as $domain => $locale) {
    Route::domain($domain)
        ->middleware('web')
        ->group(base_path("routes/web.php")); // or a per-locale closure
}

If the package exposes a route macro or helper for this (check the README — it has offered localized route registration in some versions), prefer it so URL generation stays consistent.

Generating cross-domain URLs

route() / URL::action() generate against the current request host by default. For a language switcher, an hreflang block, or transactional emails, you need to force the target domain. Two reliable options:

  • Pass the fully-qualified URL explicitly: route('home', [], true) after temporarily swapping the domain, or
  • Use the package's URL helper (e.g. a Localization facade/service method that accepts a locale) to resolve locale → domain → URL.

Wrap this in a single helper (localized_route($name, $params, $locale)) so controllers and Blade views never assemble domains by hand.

Language switcher

Map the current route to the same route on the target domain, preserving path parameters and query string. Redirect (302) rather than rendering the same content on a second host — duplicate content on multiple ccTLDs hurts SEO unless you emit hreflang.

Canonical + hreflang

Emit <link rel="canonical"> pointing at the current domain, and one <link rel="alternate" hreflang="xx"> per configured locale. Since the domain map already lives in config, a Blade partial can generate both from a single loop.

Testing

Host-based behaviour is easy to test without DNS:

$this->withServerVariables(['HTTP_HOST' => 'example.fr'])->get('/');
$this->assertSame('fr', app()->getLocale());

Add a data-provider test that iterates every configured domain and asserts both the locale and the generated home URL. This catches config typos before deployment.

Behind a proxy / load balancer

If TLS terminates upstream, $request->getHost() reflects the internal host unless TrustProxies is configured for X-Forwarded-Host. Confirm the middleware reads the forwarded host, not SERVER_NAME.

Gotchas and Tips

Ports and www in local dev

example.com:8000 is not the same string as example.com. If the middleware does an exact host match, dev servers on a non-standard port silently fall back to the default locale. Same risk with www.example.com vs example.com — normalize (strip port, optionally strip www) before matching, or maintain both keys in the map.

IDN / punycode

Internationalized ccTLDs (example.中国) arrive punycoded in HTTP_HOST. Store the ASCII form in config, not the Unicode form.

Config cache

After editing domains, run php artisan config:clear (and config:cache in deploy scripts). A stale cached map is the most common "my new locale 404s" report.

Route caching

If your per-domain route registration uses closures inside a loop, php artisan route:cache may fail or silently produce wrong domains. Extract route definitions to files or invokable controllers, then verify with route:list that every domain/name pair exists.

Session and cookie scope

This is the big architectural constraint of domain localization: cookies cannot be shared across different registrable domains. example.com and example.fr have separate sessions by default. If users must stay logged in across locales, either (a) use subdomains of one domain instead of ccTLDs, (b) set SESSION_DOMAIN=.example.com only if all locales are subdomains, or (c) accept re-authentication on switch. Do not assume the package handles this — it resolves locale, not identity.

Locale precedence

Decide explicitly whether the domain, the session, or Accept-Language wins. Domain should almost always win for a ccTLD site; otherwise a user who once picked fr on example.com gets French content on the .com domain. If you have both this package and a session-based locale switcher, remove the ambiguity.

Unconfigured hosts

Requests to an unknown host (staging URL, bare IP, preview deployments) will fall through to the default locale or throw. Add a catch-all or a middleware guard that returns a 404 or redirects to the primary domain — otherwise preview environments quietly serve the wrong language.

Redirect loops

If you add canonical-host redirects (e.g. force www), ensure the language middleware runs after the redirect and does not itself redirect to a host that the canonical middleware then rewrites back. Keep exactly one middleware responsible for host canonicalization.

Extension points and maturity

At ~27 stars and no dependents, expect thin documentation and no ecosystem of extensions. Plan to read the middleware and config classes directly (they're short). If you need custom resolution logic — geo-IP fallback, path-prefix + domain hybrid — bind your own resolver or subclass the package's locale handling rather than forking; the package is small enough that a two-file override is usually cleaner than a patch. Pin the version in composer.json and add the host-to-locale data-provider test above as your regression net for upgrades.

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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums