---
title: "Answer human-readable text in the language · Agent Wranglers"
url: https://agentwranglers.se/playbook/localization/apis-and-data/response-language
id: LOC-33
level: Recommended
applies: public-api
updated: 2026-10-22
tokens: 936
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Answer human-readable text in the language the client asked for

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [APIs and data](https://agentwranglers.se/playbook/localization/apis-and-data.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers.md). Updated 2026-10-22.

- ID: LOC-33
- Level: Recommended
- Chapter: [APIs and data](https://agentwranglers.se/playbook/localization/apis-and-data.md)
- Rule: When an API returns text for people, choose its language from an explicit setting or parameter first, then `Accept-Language`. Answer with `Content-Language`, and add `Accept-Language` to `Vary` when it chose the language.
- Check: Request the same endpoint with `Accept-Language: de` and with `sv`. Each answer has the matching `Content-Language`, the text differs, and `Vary` lists `Accept-Language`.
- Applies when: The project offers an API to others.
- How: Error titles, product names and generated text are the usual cases. Resolve the language with the chain from [Fall back to the closest supported locale](https://agentwranglers.se/playbook/localization/locales-and-preferences/locale-fallback.md). A user's saved language wins over the header, because a server job or an agent may send none. `Content-Language` tells the client what it got. `Vary: Accept-Language` stops a cache from serving German to everyone after one German request.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
// Express
app.use((req, res, next) => {
  req.locale = req.user?.locale ?? req.acceptsLanguages(...SUPPORTED) ?? DEFAULT_LOCALE;
  res.set('Content-Language', req.locale);
  res.vary('Accept-Language');
  next();
});
```

### Python example

```python
# FastAPI
from babel import negotiate_locale

@app.middleware("http")
async def response_language(request, call_next):
    wanted = [part.split(";")[0].strip().replace("-", "_") for part in request.headers.get("accept-language", "").split(",") if part]
    request.state.locale = negotiate_locale(wanted, SUPPORTED) or DEFAULT_LOCALE
    response = await call_next(request)
    response.headers["Content-Language"] = request.state.locale.replace("_", "-")
    response.headers.append("Vary", "Accept-Language")
    return response
```

## Sources

- [RFC 9110 (HTTP Semantics, 12.5.4 Accept-Language)](https://www.rfc-editor.org/rfc/rfc9110#section-12.5.4)
- [RFC 9110 (8.5 Content-Language)](https://www.rfc-editor.org/rfc/rfc9110#section-8.5)
- [RFC 9110 (12.5.5 Vary)](https://www.rfc-editor.org/rfc/rfc9110#section-12.5.5)

## Related rules

- LOC-23 [Fall back to the closest supported locale](https://agentwranglers.se/playbook/localization/locales-and-preferences/locale-fallback.md) in Localization

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/localization/apis-and-data/response-language">Agent Wranglers localization playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Send raw values with standard codes instead of formatted text](https://agentwranglers.se/playbook/localization/apis-and-data/locale-neutral-api-values.md)
- Next: [Give every error a code that clients can translate](https://agentwranglers.se/playbook/localization/apis-and-data/error-codes-for-translation.md)
- [APIs and data](https://agentwranglers.se/playbook/localization/apis-and-data.md)
