From d326aa28861ab9cd52d797111760c7f43a68af43 Mon Sep 17 00:00:00 2001 From: Abdul Rafey Date: Fri, 7 Aug 2026 19:35:29 +0500 Subject: [PATCH 1/2] docs: document the preferred language setting Covers the supported list, how to read and set a user's choice, what null means, and what happens to a stored tag once its language leaves the allowlist. The guide is explicit that generated content does not consume the preference yet, so nothing here promises behaviour the platform does not have. The DEFAULT_LANGUAGE reference entry ships with the setting itself, so this change is guide-only. Co-Authored-By: Claude Sonnet 5 --- docs/guides/user-management.md | 77 ++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/docs/guides/user-management.md b/docs/guides/user-management.md index bc95937d..f33179d1 100644 --- a/docs/guides/user-management.md +++ b/docs/guides/user-management.md @@ -53,3 +53,80 @@ Options: - `identifier`: Username or email of the user (required, positional) - `--new-password, -p`: New password (optional, will prompt if not provided) + +## Preferred language + +Each user has a preferred language recorded on their profile: a +[BCP 47](https://datatracker.ietf.org/doc/html/rfc5646) tag, readable and settable +through the API. + +AI-generated course content and chat replies do not use this preference; it is +recorded and exposed for clients to read. + +### Supported languages + +| Tag | Language | +|---|---| +| `en` | English | +| `es` | Español (Spanish) | +| `fr` | Français (French) | + +The list is deliberately short. AI output quality varies by language, so a language +is added only once generated course content in it has been reviewed by a speaker. +Matching against the list is an exact, case-sensitive comparison: `en-US` and `EN` +are both rejected as unsupported, not normalised to `en`. + +Fetch the current list — and the platform default — from the API. This endpoint +requires authentication: + +```bash +curl http://localhost:7727/api/v1/languages \ + -H "Authorization: Bearer $TOKEN" +``` + +```json +{ + "languages": [ + {"code": "en", "name": "English", "native_name": "English"}, + {"code": "es", "name": "Spanish", "native_name": "Español"}, + {"code": "fr", "name": "French", "native_name": "Français"} + ], + "default": "en" +} +``` + +### Reading and setting a user's language + +`GET /api/v1/user/me` returns `language` as stored: `null` means the user has never +chosen one, which is different from having chosen English. The platform-wide +fallback value is `DEFAULT_LANGUAGE`, configurable and also readable as `default` in +the `GET /api/v1/languages` response (see the +[configuration reference](../reference/configuration.md#default_language)). + +Set it with: + +```bash +curl -X PATCH http://localhost:7727/api/v1/user/me \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"language": "es"}' +``` + +An unsupported tag is rejected with a `422`. + +Clear a previously chosen language — so the platform default applies again — +by sending an explicit `null` rather than omitting the field: + +```bash +curl -X PATCH http://localhost:7727/api/v1/user/me \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"language": null}' +``` + +The new value is stored immediately and returned by subsequent reads. + +If a language is later removed from the supported list, a stored tag that is no +longer in the list stops being a valid choice: the value stays in the column, but +it no longer passes the allowlist check, so PATCH rejects it with a `422` if the +user tries to set it again. From 4498dae5d6f1b6dfc7bd41df2b23e585895484a8 Mon Sep 17 00:00:00 2001 From: Abdul Rafey Date: Wed, 12 Aug 2026 13:25:06 +0500 Subject: [PATCH 2/2] docs: the language list is fetched without a token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follows the endpoint dropping its auth gate on #582: documenting a Bearer header the endpoint no longer wants would send readers looking for a token before they can render a sign-in page in the right language. The /user/me examples keep theirs — those do need one. Co-Authored-By: Claude Opus 5 (1M context) --- docs/guides/user-management.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/guides/user-management.md b/docs/guides/user-management.md index f33179d1..ada8aa47 100644 --- a/docs/guides/user-management.md +++ b/docs/guides/user-management.md @@ -76,12 +76,12 @@ is added only once generated course content in it has been reviewed by a speaker Matching against the list is an exact, case-sensitive comparison: `en-US` and `EN` are both rejected as unsupported, not normalised to `en`. -Fetch the current list — and the platform default — from the API. This endpoint -requires authentication: +Fetch the current list — and the platform default — from the API. This endpoint needs no +token, so the sign-in and password-reset pages can render in the right language before +anyone has one: ```bash -curl http://localhost:7727/api/v1/languages \ - -H "Authorization: Bearer $TOKEN" +curl http://localhost:7727/api/v1/languages ``` ```json