Skip to main content
Flowxi is localization-first by design. Localization is not an optional layer or a frontend concern.
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
Everything is enforced by middleware and shared across controllers, validators, and mailers.

Supported locales

Flowxi currently supports the following locales:
  • frdefault and mandatory fallback
  • en
Rules:
  • Any unsupported locale is ignored
  • Any malformed locale is ignored
  • Resolution always falls back to fr
This behavior is enforced centrally and cannot be bypassed by controllers.

Where localization happens (important)

Localization is resolved by the SetLocaleFromRequest middleware. This middleware is:
  • executed before all controllers
  • applied to all API routes
  • responsible for calling app()->setLocale(...)
Controllers do not decide the locale.
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:
  1. Custom header X-App-Locale
  2. Standard header Accept-Language
  3. Authenticated user locale (user.locale)
  4. Fallback locale (fr)
The first valid supported locale wins. No later override is possible.

1) X-App-Locale (highest priority)

This is the authoritative source of truth for locale.

Example

X-App-Locale: en
Behavior:
  • 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

Frontend rule (mandatory)

The frontend must send X-App-Locale on every request. This includes:
  • login
  • registration
  • authenticated calls
  • background refresh calls
Do not rely on backend inference or browser defaults.

2) Accept-Language (secondary)

Used only if X-App-Locale is not present.

Example

Accept-Language: en-US,en;q=0.9,fr;q=0.8
Parsing rules (exactly as implemented in middleware):
  • Header is split by ,
  • ;q= values are ignored
  • _ is normalized to -
  • Region is stripped (en-USen)
  • First supported base language wins

Resolution examples

Header valueEffective locale
en-US,en;q=0.9en
fr-FR,fr;q=0.8fr
es,pt;q=0.9fallback fr
If no supported language is found, Flowxi proceeds to the next step.

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
This is a fallback mechanism, not a preference. Typical use cases:
  • background API calls
  • internal service calls
  • recovery when headers are missing
It is never used if a locale header is present.

4) Fallback locale (guaranteed)

If no valid locale is resolved:
  • Flowxi always falls back to fr
This fallback:
  • is deterministic
  • never throws
  • guarantees a localized response in all cases
There is no scenario where a response is not localized.

Effective locale lifecycle

Once the locale is resolved:
  • app()->setLocale(locale) is executed
  • The locale is stored as effective_locale on the request
  • Controllers explicitly reuse this value
  • Mailers receive the same locale via ->locale($lang)
There is exactly one locale per request. No controller, validator, or mailer can override it later.

What is localized

The resolved locale applies to all user-facing content, including:
  • message in API responses
  • validation error messages
  • authentication and security errors
  • transactional emails:
    • magic link emails
    • email OTP codes

Example

{
  "message": "Invalid credentials.",
  "code": "INVALID_CREDENTIALS"
}
Same request with X-App-Locale: fr:
{
  "message": "Identifiants invalides.",
  "code": "INVALID_CREDENTIALS"
}
Only the message changes. The code remains identical.

What is NOT localized (by design)

The following elements are never translated:
  • code values
  • enum values
  • JSON keys
  • request field names
These are part of the API contract. Frontend logic must always rely on 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 __()
Result:
  • Email language always matches API language
  • No mismatch between UI and inbox content

Frontend integration examples

Axios

api.interceptors.request.use((config) => {
  config.headers["X-App-Locale"] = getUserLocale(); // "fr" | "en"
  return config;
});

Fetch API

fetch("/api/v1/auth/login", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-App-Locale": "en",
  },
  body: JSON.stringify(payload),
});

Authenticated requests

Authorization: Bearer <token>
X-App-Locale: fr
Never drop the locale header after login.

Frontend responsibilities

The frontend is responsible for:
  • letting the user choose a language
  • storing it client-side
  • sending it on every request
Updating 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