Laravel e le traduzioni: tra JSON, file PHP e cache da ripulire

Se usi Laravel per progetti un po’ più complessi del classico “todo list”, prima o poi ti scontri con il sistema di traduzioni. Funziona bene, è flessibile, ma ha le sue particolarità — e se non sai dove mettere le mani rischi di impazzire davanti a un “These credentials do not match our records” che continua a spuntare in inglese anche se hai già scritto la tua bella frase in italiano.

Vediamo come funziona davvero, senza fumo negli occhi.

Due strade: JSON e file PHP

Laravel supporta due approcci per le traduzioni:

1. File JSON (resources/lang/it.json)

  • È un file unico per lingua.
  • La chiave è la stringa letterale in inglese, il valore è la traduzione.
  • Esempio: { "Log in": "Accedi", "Remember me": "Ricordami" }
  • Usato soprattutto dalle viste generate da Breeze, Jetstream e compagnia bella, che scrivono direttamente __('Log in') o __('Remember me').

È semplice e veloce: traduci stringa per stringa senza namespace.

2. File PHP (resources/lang/it/auth.php, validation.php, ecc.)

  • Qui le chiavi sono logiche, non testuali.
  • Esempio per l’autenticazione: <?php return [ 'failed' => 'Le credenziali non corrispondono ai nostri dati.', 'password' => 'La password fornita non è corretta.', ];
  • In questo caso il codice chiama __('auth.failed') invece della stringa intera.

È il sistema usato da Laravel “interno”: errori di validazione, messaggi di login, reset password, ecc.

Locale e fallback

Le traduzioni vengono lette in base al locale impostato.

  • In config/app.php: 'locale' => 'it', 'fallback_locale' => 'en',
  • Oppure nel .env: APP_LOCALE=it

Attenzione: se hai fatto php artisan config:cache, il .env non viene più letto direttamente, quindi ti conviene hardcodare locale in config/app.php o ripulire la cache.

La cache, maledetta cache

Se traduzioni e locale non cambiano, il 90% delle volte è colpa della cache.
Ripulire è la prima cosa da fare:

php artisan optimize:clear
php artisan config:clear
php artisan cache:clear
php artisan view:clear

Dopo, ricarica la pagina con un hard refresh del browser.
Se vuoi essere ancora più sicuro, testa in Tinker:

php artisan tinker
>>> app()->getLocale()
"it"
>>> __('auth.failed')
"Le credenziali non corrispondono ai nostri dati."

Se qui funziona, funziona anche nel browser.

JSON o PHP? Quando usare cosa

  • JSON → per le label e i testi “visibili” nelle view (Log in, Forgot your password?).
  • PHP → per i messaggi di sistema (auth.failed, errori di validazione, reset password).

Puoi usarli anche insieme senza problemi: Laravel li fonde.

Personalizzare i messaggi di sistema

Vuoi cambiare il testo dell’errore di login? Vai in resources/lang/it/auth.php:

'failed' => 'Utente o password non validi.',

Vuoi tradurre la frase lunga del “Forgot your password?”?
Mettila nel JSON:

{
  "Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.": "Hai dimenticato la password? Nessun problema: inserisci la tua email e ti invieremo un link per reimpostarla."
}

Debugging: checklist rapida

  1. Locale impostato in config/app.php
  2. Cache ripulita (optimize:clear, config:clear, ecc.)
  3. Stringa giusta nel file giusto (auth.failed nei PHP, testi letterali nel JSON)
  4. Test con Tinker: __('auth.failed') deve restituire italiano

Conclusione

Il sistema di traduzione di Laravel è flessibile, ma non sempre intuitivo se non sai la differenza tra file JSON e file PHP namespaced.
La regola è semplice: UI nel JSON, messaggi di sistema nei PHP. E quando non funziona… la colpa è quasi sempre della cache.