It is a core backend responsibility, enforced before any controller logic runs. Every API response, validation error, authentication message, and transactional email is returned in one single, deterministic locale resolved at the very beginning of the request lifecycle. There is:
- no partial localization
- no mixed-language responses
- no runtime ambiguity
Supported locales
Flowxi currently supports the following locales:fr— default and mandatory fallbacken
- Any unsupported locale is ignored
- Any malformed locale is ignored
- Resolution always falls back to
fr
Where localization happens (important)
Localization is resolved by theSetLocaleFromRequest middleware.
This middleware is:
- executed before all controllers
- applied to all API routes
- responsible for calling
app()->setLocale(...)
They only consume the already resolved value.
Locale resolution order (strict and deterministic)
For every API request, Flowxi resolves the locale using the following order:- Custom header
X-App-Locale - Standard header
Accept-Language - Authenticated user locale (
user.locale) - Fallback locale (
fr)
1) X-App-Locale (highest priority)
This is the authoritative source of truth for locale.
Example
- Overrides all other sources
- Works for public and authenticated endpoints
-
Used consistently for:
- API
message - validation messages
- authentication & security errors
- emails sent during the request
- API
Frontend rule (mandatory)
The frontend must sendX-App-Locale on every request.
This includes:
- login
- registration
- authenticated calls
- background refresh calls
2) Accept-Language (secondary)
Used only if X-App-Locale is not present.
Example
- Header is split by
, ;q=values are ignored_is normalized to-- Region is stripped (
en-US→en) - First supported base language wins
Resolution examples
| Header value | Effective locale |
|---|---|
en-US,en;q=0.9 | en |
fr-FR,fr;q=0.8 | fr |
es,pt;q=0.9 | fallback fr |
3) Authenticated user locale (user.locale)
If no locale header is provided and the request is authenticated:
- Flowxi checks
user.locale - If it matches a supported locale, it is applied
- background API calls
- internal service calls
- recovery when headers are missing
4) Fallback locale (guaranteed)
If no valid locale is resolved:- Flowxi always falls back to
fr
- is deterministic
- never throws
- guarantees a localized response in all cases
Effective locale lifecycle
Once the locale is resolved:app()->setLocale(locale)is executed- The locale is stored as
effective_localeon the request - Controllers explicitly reuse this value
- Mailers receive the same locale via
->locale($lang)
What is localized
The resolved locale applies to all user-facing content, including:-
messagein API responses - validation error messages
- authentication and security errors
-
transactional emails:
- magic link emails
- email OTP codes
Example
X-App-Locale: fr:
code remains identical.
What is NOT localized (by design)
The following elements are never translated:codevalues- enum values
- JSON keys
- request field names
code, never on translated text.
Email localization (guaranteed)
All authentication-related emails explicitly use the resolved locale. Mechanism:- Locale resolved by middleware
-
Controller reads
effective_locale -
Emails are sent using:
Mail::to(...)->locale($lang)- localized Blade views
- translated strings via
__()
- Email language always matches API language
- No mismatch between UI and inbox content
Frontend integration examples
Axios
Fetch API
Authenticated requests
Frontend responsibilities
The frontend is responsible for:- letting the user choose a language
- storing it client-side
- sending it on every request
user.locale server-side is optional and used only as a fallback.
Guarantees
Flowxi localization guarantees:- exactly one locale per request
- no mixed-language responses
- mandatory fallback to
fr - consistent API and email language
- no reliance on browser heuristics
- deterministic, testable behavior
Testing checklist
- Send
X-App-Locale: fr→ verify French everywhere - Send
X-App-Locale: en→ verify English everywhere - Remove all locale headers → fallback to
fr - Authenticated request without headers → uses
user.locale - Verify magic link and OTP emails match API language

