// routing
Route localizzate
Definisci i locale supportati da una route, scegli come applicare i prefissi di lingua e traduci URI e domini delle route.
Come funziona
Ogni route che marchi con lang() diventa la route canonica: mantiene il suo URI e il suo nome, e serve il fallback locale. Per ogni locale aggiuntivo, il package registra una route tradotta, una copia della route canonica con:
- un URI con il prefisso del locale e, quando disponibile, tradotto tramite i tuoi file di lingua;
- un nome composto dal nome canonico più il locale, come
example.es; - il locale dell'applicazione impostato sulla sua lingua per ogni richiesta che serve.
Le route tradotte vengono generate dopo il boot di tutti i service provider, quindi definisci le tue route come sempre nei file di route e vengono rilevate automaticamente. Funzionano anche con la cache delle route.
Definire le route localizzate
Usa il metodo lang() per indicare quali locale deve supportare una route:
Route::get('/example', Controller::class)
->lang(['es', 'fr', 'it'])
->name('example');
Supponendo che il fallback locale sia en e che in lang/es/routes.php esista una traduzione spagnola per example, vengono registrate quattro route:
| URI | Nome | Locale |
|---|---|---|
/example |
example |
en |
/es/ejemplo |
example.es |
es |
/fr/example |
example.fr |
fr |
/it/example |
example.it |
it |
Le route francese e italiana non hanno una traduzione, quindi mantengono l'URI originale dopo il prefisso del locale. Vedi Tradurre gli URI delle route per aggiungerne una.
Il fallback locale
La route canonica serve il fallback_locale di Laravel (APP_FALLBACK_LOCALE nel .env). Deve corrispondere alla lingua in cui sono davvero scritti le tue route e i tuoi contenuti: se i tuoi URI sono in inglese, il fallback locale deve essere en.
Elencare il fallback locale in lang() non crea problemi: la route canonica lo serve già, quindi non viene registrata nessuna route in più.
// Same result as ->lang(['es']) when the fallback locale is 'en'
Route::get('/example', Controller::class)
->lang(['en', 'es']);
Gruppi di route
Per non ripetere le stesse lingue su ogni route, usa Route::lang()->group():
Route::lang(['es', 'it'])->group(function () {
Route::get('/example', fn () => 'Hello, World!');
Route::get('/another', fn () => 'Another route');
});
Tutte le route del gruppo ne ereditano i locale. Chiama lang() su una singola route per aggiungere altre lingue a quelle del gruppo:
Route::lang(['es', 'it'])->group(function () {
Route::get('/example', fn () => 'Hello, World!'); // es, it
Route::get('/another', fn () => 'Another route')
->lang(['fr']); // es, it, fr
});
lang() si combina con gli altri attributi dei gruppi, come middleware, prefissi e domini:
Route::middleware('auth')->lang(['es', 'it'])->group(function () {
Route::get('/dashboard', DashboardController::class)->name('dashboard');
});
Il locale dell'applicazione
Ogni route localizzata imposta il locale dell'applicazione sulla propria lingua prima che venga eseguito il tuo codice:
- una route tradotta usa il suo locale, quindi
/es/ejemploviene eseguita conapp()->getLocale()uguale aes; - la route canonica usa il fallback locale, quindi
/exampleviene eseguita conen; - le route senza
lang()lasciano invariato il locale dell'applicazione, che per impostazione predefinita èAPP_LOCALE.
Questo significa che __(), i messaggi di validazione, le date e tutto ciò che dipende dal locale seguono automaticamente l'URL. Con una traduzione spagnola in lang/es.json:
Route::get('/welcome', fn () => __('Welcome!'))
->lang(['es']);
// GET /welcome → "Welcome!"
// GET /es/welcome → "¡Bienvenido!"
Il locale è già impostato quando viene eseguito il route model binding, quindi i tuoi binding possono risolvere slug tradotti o altri valori che dipendono dal locale.
Per leggere il locale della route corrente, usa $request->route()->locale(). Vedi Helper delle route.
Nomi delle route
Le route tradotte prendono il nome della route canonica, seguito da un punto e dal locale:
Route::get('/example', Controller::class)
->lang(['es', 'it'])
->name('example');
// Registered names: example, example.es, example.it
Raramente ti servono questi nomi direttamente. Chiama route('example') e il package sceglie la route per il locale corrente, oppure passa lang per sceglierne un'altra. Vedi Generazione degli URL.
Per controllare la route corrente, usa L10n::is('example') invece di Route::is('example'): corrisponde alla route canonica e a tutte le sue traduzioni. Vedi Corrispondenza delle route.
Anche le route senza nome vengono localizzate: il package assegna a loro, e alle loro traduzioni, un nome interno che inizia con generated::.
Strategia di routing
L'opzione route_strategy in config/l10n.php controlla come vengono applicati i prefissi di lingua. Le strategie sono tre:
prefix_except_default(predefinita) lascia senza prefisso il fallback locale e aggiunge il prefisso a tutti gli altri locale.prefixaggiunge il prefisso a ogni locale, compreso il fallback, e non registra nessuna route senza prefisso.no_prefixusa gli URI tradotti senza alcun prefisso di lingua.
Ecco come si confrontano per Route::get('/example')->lang(['es', 'it']), con una traduzione spagnola per example e nessuna italiana:
| Locale | prefix_except_default |
prefix |
no_prefix |
|---|---|---|---|
en (fallback) |
/example |
/en/example |
/example |
es |
/es/ejemplo |
/es/ejemplo |
/ejemplo |
it |
/it/example |
/it/example |
non registrata |
Con prefix, una richiesta a /example restituisce 404: il fallback locale si trova su /en/example come qualsiasi altra lingua. Con no_prefix, la route italiana avrebbe lo stesso URI di quella canonica, quindi viene saltata (vedi Collisioni con no_prefix in Tradurre gli URI delle route).
Per cambiare strategia, pubblica la configurazione del package:
php artisan vendor:publish --tag=l10n-config
Poi imposta l'opzione nel file pubblicato:
return [
'route_strategy' => 'prefix',
];
La strategia vale per tutte le route localizzate dell'applicazione.
Tradurre gli URI delle route
Gestisci le traduzioni delle route in un file routes.php per ogni lingua, dentro la cartella lang:
/lang
├── /es
│ └── routes.php
├── /fr
│ └── routes.php
└── /it
└── routes.php
In ogni file, associa l'URI originale della route alla sua traduzione:
return [
'example' => 'ejemplo',
'products/{product}' => 'productos/{product}',
];
Se per un locale non c'è una traduzione, l'URI originale viene usato così com'è.
Mantieni nell'URI tradotto i parametri della route con gli stessi nomi, così la route tradotta riceve gli stessi parametri di quella canonica. Vanno tradotti solo i segmenti statici.
Chiavi di binding personalizzate
Per le route con una chiave di binding personalizzata, ometti la chiave (:slug) sia dalla chiave di traduzione sia dall'URI tradotto:
Route::get('/article/{post:slug}', [PostController::class, 'show'])
->lang(['it']);
return [
'article/{post}' => 'articolo/{post}',
];
La route localizzata continua a risolvere post tramite il suo slug.
Chiavi di route tradotte
Se è la chiave della route a essere tradotta, per esempio uno slug salvato per ogni locale, implementa il contratto HasLocalizedRouteKey sul model. Quando genera l'URL di una route localizzata, il package chiama getLocalizedRouteKey() passando il locale della route e il campo di binding. Questo esempio usa spatie/laravel-translatable:
use Goodcat\L10n\Contracts\HasLocalizedRouteKey;
use Spatie\Translatable\HasTranslations;
class Post extends Model implements HasLocalizedRouteKey
{
use HasTranslations;
public array $translatable = ['slug'];
public function getLocalizedRouteKey(string $locale, ?string $field = null): ?string
{
return $this->getTranslation($field ?? $this->getRouteKeyName(), $locale, false) ?: null;
}
}
route('posts.show', ['post' => $post, 'lang' => 'it']); // "https://example.com/it/articolo/ciao-mondo"
Restituisci null quando il model non ha una traduzione per un locale. Il selettore di lingua, i link hreflang e il redirect al locale preferito saltano quel locale. Per route() e action(), vedi Chiavi di route localizzate mancanti.
Le richieste in ingresso vengono risolte dal binding del model. Per rispondere con un 404 quando il post non ha una traduzione, o quando il suo slug appartiene a un altro locale, cerca lo slug nel locale della route:
public function resolveRouteBinding($value, $field = null): ?self
{
$field ??= $this->getRouteKeyName();
return $this->where("$field->".app()->getLocale(), $value)->first();
}
Per fare invece un redirect, lascia che il binding trovi lo slug in qualsiasi locale e manda il visitatore all'URL corretto:
public function show(Request $request, Post $post)
{
if ($request->route()->originalParameter('post') !== $post->getLocalizedRouteKey(app()->getLocale())) {
return redirect()->route('posts.show', $post, 301);
}
// ...
}
Collisioni con no_prefix
Senza un prefisso di lingua, due route possono finire con lo stesso URI. Con la strategia no_prefix, una traduzione che condivide dominio e URI con la route canonica o con una traduzione precedente viene saltata:
// lang/es/routes.php has no translation for 'dashboard'
Route::get('/dashboard', DashboardController::class)
->lang(['es'])
->name('dashboard');
Qui /dashboard è servita solo dalla route canonica, con il fallback locale. Anche route('dashboard', ['lang' => 'es']) restituisce l'URL canonico. Per servire i visitatori spagnoli su un URL dedicato, aggiungi una traduzione per dashboard in lang/es/routes.php.
Traduzione dei domini
Se la tua applicazione usa il routing basato sui domini, puoi tradurre i domini negli stessi file di lingua routes.php. La chiave è il dominio originale:
return [
'example' => 'ejemplo',
'example.com' => 'es.example.com',
];
Quando un locale ha un dominio tradotto, le sue route non hanno prefisso: è già il dominio a identificare la lingua. I locale senza un dominio tradotto mantengono il prefisso nel percorso sul dominio originale:
Route::domain('example.com')->lang(['es', 'it'])->group(function () {
Route::get('/example', Controller::class);
});
| Locale | prefix_except_default |
prefix |
|---|---|---|
en (fallback) |
example.com/example |
example.com/en/example |
es (dominio tradotto) |
es.example.com/ejemplo |
es.example.com/ejemplo |
it |
example.com/it/example |
example.com/it/example |
Con la strategia prefix, il fallback locale mantiene il prefisso nel percorso sul dominio originale, perché non ha un dominio tradotto.
Cosa ereditano le route tradotte
Una route tradotta è una copia della sua route canonica, quindi mantiene tutto quello che hai definito su di essa:
- il controller o la closure;
- i middleware, compresi quelli ereditati dai gruppi;
- i vincoli sui parametri definiti con
where()e i valori predefiniti definiti condefaults(); - le opzioni del route model binding, come le chiavi personalizzate e
withTrashed(); - le route
Route::fallback(), le cui traduzioni sono anch'esse route di fallback.
Definisci la route una volta sola e ogni lingua si comporta allo stesso modo, cambiano solo URL e locale.