skip to content

In Laravel 13, where is the application's default locale configured, and how do you switch the locale for the current request?

level: juniorimportance: must knowfreq 55%

answer

  1. a key in config/app.php
  2. APP_LOCALE in .env, default en
  3. a setter on the App facade
  4. currentLocale() and isLocale()
  5. nothing is persisted

basics

~10 s

The default is the locale key in config/app.php, read from APP_LOCALE and defaulting to en. App::setLocale('es') switches it for the rest of the current request; App::currentLocale() and App::isLocale('es') read it back.

solid answer

~40 s

The skeleton's `config/app.php` holds `'locale' => env('APP_LOCALE', 'en')`, so you normally set `APP_LOCALE` in `.env` rather than editing the file. At runtime `App::setLocale('es')` switches it: it writes `app.locale` in the in-memory config, sets the translator's locale, and dispatches a `LocaleUpdated` event. `App::currentLocale()`, an alias of `getLocale()`, returns the value and `App::isLocale('es')` compares against it. Nothing is stored: the next request boots from config again, so a per-user or per-URL choice has to be re-applied on every request, usually in middleware. `setLocale` rejects only values containing a slash, a backslash, `..` or a null byte, so check user input against your supported list first.

code

php · 13 lines
php
<?php

use Illuminate\Support\Facades\App;

App::currentLocale();   // 'en' (from APP_LOCALE)

App::setLocale('de');

App::currentLocale();   // 'de'
App::isLocale('de');    // true
__('hr.leave_request'); // read from lang/de/hr.php

// the next request starts again at 'en'

go deeper

for a junior

Recall config/app.php's locale key, APP_LOCALE in .env, App::setLocale() for the current request, and currentLocale()/isLocale() to read it.

for a middle

Explain what setLocale() does in order: config value, translator locale, LocaleUpdated event, and why nothing persists between requests.

for a senior

Point out the traps that ship bugs: request-level setLocale, raw config writes after the translator is built, env() in code, and unvalidated user input.

for a principal

Decide where locale state lives across web requests, APIs and background work so every entry point applies it the same way.

## The configured default Every Laravel application has one **default locale**, the language used for translations when nothing else has chosen one. In the Laravel 13 skeleton it lives in `config/app.php`: ```php 'locale' => env('APP_LOCALE', 'en'), ``` So the usual way to change it is an entry in `.env`: ```ini APP_LOCALE=de ``` Because `env()` is called inside a config file, the value survives `php artisan config:cache`: the cached config already holds the resolved string. ## Switching at runtime `App::setLocale($locale)`, the facade call for the application's `setLocale()` method, changes the locale for the rest of the current request. In order, it: 1. writes the new value to the in-memory config key `app.locale`; 2. sets the translator's current locale, so later `__()` calls use it; the translator throws an `InvalidArgumentException` here for a value containing `/`, a backslash, `..` or a null byte; 3. dispatches `Illuminate\Foundation\Events\LocaleUpdated`, carrying the new `locale` and the `previousLocale`. Anything listening for that event can follow the change. Carbon's own Laravel service provider, which is package-discovered, listens for it and switches Carbon's locale to match. ## Reading the locale | Call | Returns | |---|---| | `App::currentLocale()` | the current value of `app.locale` | | `App::getLocale()` | the same value; `currentLocale()` is an alias | | `App::isLocale('de')` | `true` when the current locale equals the argument | | `App::getFallbackLocale()` | the value of `app.fallback_locale` | Use these rather than `config('app.locale')` so code reads as intent and survives a future change of storage. ## Why the choice does not persist Under PHP-FPM each request boots the application from scratch and reads `app.locale` from config again. `setLocale` changes memory only; it writes no cookie, session value or database row. A user's choice therefore has to be **stored somewhere** and **re-applied on every request**: typically a middleware reads a user column, a URL segment or the `Accept-Language` header and calls `App::setLocale()` before the controller runs. ## Validating input `setLocale` accepts any string without path characters, including codes you have no translations for, such as `xx`. The translator then misses every line and falls back, and pluralization rules for an unknown code choose the first form. Always check a user-supplied value against a list of supported locales before passing it on. ## Pitfalls - **`$request->setLocale('de')`** sets the locale property of the HTTP request object (inherited from Symfony's request class); it does not touch the translator, so `__()` output is unchanged. - **`config(['app.locale' => 'de'])`** writes config but, once the translator has been built, does not update it; only `App::setLocale()` does both and fires the event. - **Calling `env('APP_LOCALE')` in application code** returns `null` once config is cached, because `.env` is no longer loaded; read the config value or `App::currentLocale()` instead. - **Setting the locale in a controller** works for that action but is easy to forget elsewhere; a middleware keeps it in one place. ## Where it fits in a request A typical request on a multilingual site goes through these stages: 1. The application boots and the translator starts at `app.locale`. 2. A middleware decides the request's language from a stored preference, a URL segment or a header, and calls `App::setLocale()`. 3. Controllers, views, validation messages and anything else that translates now use that locale. 4. The response is sent and the in-memory state is discarded. Because stage 2 happens on every request, the decision must be cheap: read a column already loaded with the user, a route parameter or a header, not a remote service. ## Checking it on a running app - `php artisan about` lists the configured `Locale` among the environment details. - `php artisan config:show app` prints the whole `app` config, including `locale`, `fallback_locale` and `faker_locale`. - In a feature test, call `App::setLocale('de')` inside the test, request a page and assert the German text, so the expectation is explicit rather than dependent on `.env`.

  • How can other code react when the locale changes mid-request?
    Listen for `Illuminate\Foundation\Events\LocaleUpdated`, which `App::setLocale()` dispatches with the new `locale` and the `previousLocale`. Carbon's package-discovered service provider already listens for it and switches Carbon's locale; your own listener could, for example, reconfigure a formatting service.
  • What happens if a request calls App::setLocale('../etc')?
    The translator's `setLocale()` throws an `InvalidArgumentException` for values containing a slash, a backslash, `..` or a null byte, because the locale becomes part of a file path. Any other string is accepted, which is why you still need an allowlist.
  • Why is isLocale('de') preferable to comparing config('app.locale') directly?
    It reads as intent and goes through the application's own accessor, so the check keeps working however the locale is stored. Functionally both compare the current `app.locale` value.

saying these in an interview costs you the question

  • App::setLocale() saves the choice so later requests use it too
  • $request->setLocale('de') changes the language of __() output
  • config(['app.locale' => 'de']) always updates the translator
  • setLocale() throws for any locale without a lang folder
  • The default locale is set in bootstrap/app.php