diff --git a/docs/api.md b/docs/api.md index b09127c..64ec911 100644 --- a/docs/api.md +++ b/docs/api.md @@ -114,7 +114,8 @@ "birth_date": "1990-01-01", "country": "US", "city": "New York", - "locale": "en-US", + "locale": null, + "locale_effective": "en-US", "avatar_url": "https://...", "profile_url": "https://...", "role": "admin", @@ -122,11 +123,39 @@ } ``` +`locale` is the interface language chosen in Settings — `null` (or `"auto"`) means +"follow the account". `locale_effective` is the language the interface actually uses: +that choice, else the gnexus-auth account locale, else `en`. The account's own tag is +not part of this payload. + **Errors** - `401` — not authenticated --- +#### `PATCH /auth/me` + +Choose the interface language for the current user. + +**Request** +```json +{ "locale": "ru" } +``` + +`en`, `uk`, `ru` (or a regional tag like `en-US`, which normalizes to `en`) set the +choice; `""` or `"auto"` clear it, so the account language applies again. The choice +lives in `navi_users.locale_override` and is untouched by the `user.profile_updated` +webhook, which only mirrors the account locale. + +**Response `200`** — the same body as `GET /auth/me` + +**Errors** +- `401` — not authenticated +- `422` — a language Navi does not ship +- `503` — the choice could not be stored + +--- + #### `GET /auth/status` Return whether auth is enabled on the backend and whether OAuth is configured. diff --git a/docs/i18n.md b/docs/i18n.md new file mode 100644 index 0000000..6a308d8 --- /dev/null +++ b/docs/i18n.md @@ -0,0 +1,70 @@ +# Interface language (i18n) + +Navi's web client speaks **en / uk / ru**. The rules themselves live in the +platform handbook ([handbook.gnexus](https://handbook.gnexus) → `10-platform/i18n`); +this file only records how navi follows them and what is still untranslated. + +## Where the language comes from + +`locale_effective` from `GET /auth/me` — the choice made in Settings +(`navi_users.locale_override`) → the gnexus-auth account locale +(`navi_users.locale`, a straight mirror kept by the `user.profile_updated` +webhook) → `en`. + +The picker is the first block of **Settings → Account** +(`webclient/src/components/settings/AccountPanel.vue`) and offers +Auto / English / Українська / Русский — each language named in its own language. +A choice is written with `PATCH /auth/me`, so it follows the account to other +devices and survives logout. + +With `NAVI_AUTH_ENABLED=false` there is no account to follow: the picker drops +Auto, the choice is kept in `localStorage` under `navi.locale`, and the same key +is used when the visitor is not signed in. + +## The module + +`webclient/src/i18n/` — vue-i18n is deliberately not pulled in: + +| File | Role | +|---|---| +| `index.js` | `locale` ref, `t(slug, params)`, `setLocale`, `normalizeTag`, `pluralCategory`, and the `navi.locale` storage helpers | +| `messages/en.js`, `uk.js`, `ru.js` | flat dictionaries, the same slugs in all three | + +- `t()` interpolates `{param}` and falls back to the **English** string when the + chosen language has no slug yet (or to the slug itself when nobody has it). +- Plurals are dictionaries (`{ one, few, many, other }`) driven only by + `params.n`; ru/uk use one/few/many, en uses one/other. +- A slug is `.` and an area is a screen or a component group — + `settings.*`, `sidebar.*`, `messages.*`, `chat.*`, `artifacts.*`, `ui.*`, + `profile.*`, `app.*`, `common.*`. Slugs are shared with the other gnexus + services where they mean the same thing. +- `SUPPORTED_LOCALES` is the single list: adding a language means adding + `messages/.js` there, plus `navi/locales.py` on the backend (and the + same slug in `navi_users.locale_override` constraints on the client side). +- Lists of options or nav items must be **computed**, not module-level + constants, or their labels freeze in the language that was active at import. + +The backend side is `navi/locales.py` (`normalize_locale`, `effective_locale`), +mirroring the frontend's normalisation: `en-US`/`en_US` → `en`, anything not +shipped → `None`. + +## Translation status + +Phase 1 shipped the framework and the language block only. Everything else is +still hard-coded English and is translated area by area, biggest first: + +| Area | Strings | Status | +|---|---|---| +| `settings.*` | ~118 | language block only | +| `artifacts.*` | ~45 | pending | +| `sidebar.*` | ~29 | pending | +| `messages.*` | ~27 | pending | +| `chat.*` | ~21 | pending | +| `ui.*` | ~20 | pending | + +The remaining strings are counted in templates (text nodes, `placeholder`, +`title`, `aria-label`, `label`, `alt`) plus keyed strings in ` + + diff --git a/webclient/src/components/ui/LoginScreen.vue b/webclient/src/components/ui/LoginScreen.vue index 5ef0943..9a25f4e 100644 --- a/webclient/src/components/ui/LoginScreen.vue +++ b/webclient/src/components/ui/LoginScreen.vue @@ -4,7 +4,7 @@

Welcome to Navi

-