|
1 | 1 | # July 2026 |
2 | 2 |
|
| 3 | +- [breaking] **The theme is one file, translated through gettext.** |
| 4 | + `theme/config.json` replaces the 30 per-locale copies, and its text is |
| 5 | + translated via the `theme` gettext domain. Two thirds of each copy was |
| 6 | + structure rather than text, and they drifted — a `theme_color` added to |
| 7 | + English never reached the other 29. Configuration (colours, hrefs, icons, |
| 8 | + layout) is now structurally untranslatable. Extract with |
| 9 | + `mix gamend.theme.extract`; migrate existing per-locale files with |
| 10 | + `mix gamend.theme.migrate_locales`. `modules/example_config*.json` is gone — |
| 11 | + nothing loaded it. |
| 12 | +- [breaking] **Quest and leaderboard translations are no longer stored in the |
| 13 | + database.** The per-locale `metadata["titles"][locale]` / |
| 14 | + `["descriptions"][locale]` copies, their admin editor and |
| 15 | + `Quest.localized_title/2` (plus the `Leaderboard` equivalents) are removed — |
| 16 | + the same duplicate-store problem the theme had. A row now holds only its |
| 17 | + source text and gettext translates it at render. |
| 18 | + `mix gamend.content.migrate_metadata` lifts existing per-locale metadata into |
| 19 | + `content.po` (`--dry-run` to preview, `--prune` to clear the dead keys). |
| 20 | +- [added] **Quest, leaderboard and tournament text is translatable.** |
| 21 | + `mix gamend.content.extract` reads the titles and descriptions from the |
| 22 | + database into a `content` domain, so plugin-defined *and* admin-created rows |
| 23 | + are covered; `GameServerWeb.ContentText` translates them per viewer. Admin |
| 24 | + pages deliberately keep showing the stored source string. |
| 25 | +- [added] **All 30 locales are fully translated** (352 strings each, up from |
| 26 | + 266) — machine-translated through the CSV round-trip and awaiting native |
| 27 | + review. |
| 28 | +- [changed] `mix gettext.export_csv` / `import_csv` cover both gettext trees |
| 29 | + and every domain; the `_config` pseudo-domain and `--config` flag are gone, |
| 30 | + since theme text is now a normal PO domain. |
| 31 | +- [added] **Quest chains are browsable** — clicking a chained quest shows every |
| 32 | + tier, including locked ones ahead. |
| 33 | +- [breaking] **A chain lists as one quest.** `list_user_quests` (and |
| 34 | + `GET /quests/me`) return a single entry per chain — the earliest tier not yet |
| 35 | + claimed; once all are claimed, the final tier. Previously a finished tier and |
| 36 | + its unlocked successor listed side by side. |
| 37 | +- [breaking] **String fields in API responses are never `null`** — an unset |
| 38 | + value serializes as `""` (Godot clients choke on `null` where a string is |
| 39 | + expected). Applies to REST, realtime payloads and struct-encoded responses |
| 40 | + alike: `icon_url`, `description`, `recur`, `prerequisite_quest_key`, |
| 41 | + notification `sender_id`, and friends. Datetimes/numbers keep `null` where |
| 42 | + absence is semantic. |
| 43 | +- [added] `icon_url` on **notifications, tournaments, groups and leaderboards** |
| 44 | + (quests already had it). Group admins upload via `POST /groups/:id/icon/upload_url` + |
| 45 | + `POST /groups/:id/icon`; tournaments and leaderboards take a URL in admin. |
| 46 | + Entities without an icon fall back to one shared default per type, from the |
| 47 | + typed icon set in `GameServerWeb.Icons`. |
| 48 | +- [breaking] **API route paths use underscores throughout.** The API mixed the |
| 49 | + two styles (`/me/push-tokens` next to `/groups/:id/join_requests`); every |
| 50 | + hyphenated path is renamed, with no redirects. Notably `/users/log-in`, |
| 51 | + `/users/log-out`, `/users/update-password`, |
| 52 | + `/users/settings/confirm-email/:token`, `/me/push-tokens`, |
| 53 | + `/me/friend-requests`, `/me/avatar/upload-url`, `/data-deletion`, |
| 54 | + `/tournaments/:id/my-match`, `/economy/grant-item`, `/economy/consume-item` |
| 55 | + and the `/admin/rate-limiting` and `/admin/lobby-snapshots` pages. **Update |
| 56 | + any external registration of `/data-deletion`** (Meta/Facebook app settings), |
| 57 | + and note that confirmation links already emailed under the old path will 404. |
| 58 | +- [added] **Two-step icon upload for tournaments, leaderboards and quests** |
| 59 | + (admins), matching the flow groups and avatars already had: |
| 60 | + `POST .../icon/upload_url` returns a presigned ticket, the client PUTs the |
| 61 | + image straight to storage, `POST .../icon` confirms the key. The mechanism |
| 62 | + now lives in one place, `GameServerWeb.Uploads`, which also confines a |
| 63 | + client-supplied key to its own entity's prefix. |
| 64 | +- [fixed] Admin `GET`/`PATCH /api/v1/admin/tournaments` documented `icon_url` |
| 65 | + in its OpenAPI schema but never returned it. |
| 66 | +- [fixed] **Uploaded icons are cached like avatars.** `icons/` now carries the |
| 67 | + same immutable one-year `Cache-Control` as `avatars/` — every icon key is |
| 68 | + content-unique (`Storage.build_key/3`), so revalidating one on each page load |
| 69 | + was pure waste. |
3 | 70 | - [added] **Settings**: one declared config surface. |
4 | 71 | - [changed] **Guides are markdown files.** `/docs/setup` |
5 | 72 | - [changed] **Times are shown in the reader's timezone.** |
|
0 commit comments