Response localization
Localize what Lararouter writes with Accept-Language, not what the model says.
Send Accept-Language and Lararouter localizes the text it authors: error messages, error hints, model
descriptions, and limit descriptions.
curl https://REGION.api.lararouter.com/v1/batches \
-H "Authorization: Bearer $LARAROUTER_API_KEY" \
-H "Accept-Language: fr-FR, fr;q=0.9, en;q=0.5" \
-H "Content-Type: application/json" \
-d '{ "input_file_id": "file_9Hs4TnQ2", "endpoint": "/v1/chat/completions" }'{
"error": {
"type": "invalid_request_error",
"code": "model_not_found",
"message": "Le modèle « llama-4-500b » n'existe pas.",
"param": "model"
}
}It does not change model output
Accept-Language never reaches the model. Asking for fr-FR and getting an English completion is
correct behaviour. The language a model answers in is set by your prompt, not by an HTTP header.
That distinction catches people out often enough to be worth stating twice. The header controls Lararouter's strings. If you want French completions, say so in the system message on the line:
{
"model": "llama-3.3-70b",
"messages": [
{ "role": "system", "content": "Réponds toujours en français." },
{ "role": "user", "content": "Résume ce ticket." }
]
}Negotiation
Standard RFC 9110 negotiation with quality values, over these locales:
| Locale | Language |
|---|---|
en | English |
es | Spanish |
fr | French |
de | German |
pt | Portuguese |
hi | Hindi |
Region subtags are accepted and matched down to the base language, so pt-BR resolves to pt.
Anything with no match falls back to English rather than erroring.
Every response echoes what was actually used:
POST /v1/batches HTTP/1.1
Host: REGION.api.lararouter.com
Authorization: Bearer $LARAROUTER_API_KEY
Accept-Language: fr-FR, fr;q=0.9, en;q=0.5
Content-Type: application/json
{
"input_file_id": "file_9Hs4TnQ2",
"endpoint": "/v1/chat/completions",
"completion_window": "24h"
}
HTTP/1.1 404 Not Found
Content-Language: frCheck that header rather than assuming the request was honoured. Accept-Language: nl returns
Content-Language: en, and the difference is the whole answer to "why is this still in English."
What gets localized
Prop
Type
Match on error.code, display error.message. Matching on message text breaks the moment a locale
changes, and localization makes that a certainty rather than a risk.
Passing through a user's locale
The useful pattern is forwarding the locale you already resolved for the current user, so a budget error surfaces in their language without a translation layer of your own.
use Throwable;
try {
$response = Http::withToken(config('services.lararouter.key'))
->withHeaders(['Accept-Language' => app()->getLocale()])
->post('https://REGION.api.lararouter.com/v1/batches', $payload)
->throw();
} catch (Throwable $e) {
// Already in the user's language.
return back()->withErrors(['ai' => $response->json('error.message')]);
}Forwarding the browser's raw Accept-Language works too. It's already a valid header, quality values
and all.