Translate the front with a language provider (English + French) #242

Closed
opened 2026-10-05 08:17:34 +00:00 by root · 30 comments
root commented 2026-10-05 08:17:34 +00:00 (Migrated from gitlab.core-project.space)

Imported from GitHub amaurycoudr/chess-project#242, opened by @amaurycoudr on 2026-10-05 08:17 UTC.
Closed on GitHub on 2026-10-05 10:11 UTC as completed.


Problem Statement

Every word the app shows is hard-coded English, spread over constants in each vertical (status lines, refusals, confirmations, presence, offers, home). A French-speaking player cannot use the app in French, and changing any wording means hunting through components.

Solution

The front reads all its copy from one message catalogue, in English and French. The language follows the browser on first visit and can be switched from the site header; the choice is remembered on that device. The library is Paraglide JS (inlang): messages compile to typed functions, unused ones are tree-shaken, and it ships a Vite plugin.

User Stories

  1. As a player whose browser is in French, I want the app in French on my first visit, so that I don't have to look for a setting.
  2. As a player whose browser is in any other language, I want the app in English, so that I always get a complete language.
  3. As a player, I want to switch between English and French from the site header, so that I can override my browser.
  4. As a player, I want my choice remembered on this device, so that I don't switch again on every visit.
  5. As a player, I want every message translated — status lines, actions and their confirmations, offers, presence, toasts, refusals, home, local game — so that no English is left behind in French.
  6. As a screen-reader user, I want the page's lang to match the language shown, so that it is read with the right voice.

Seams

  • Views through each vertical's harness and MSW (view tests): the home, local and room views rendered with a given browser language and with a stored choice. Covers stories 1–6.

Implementation Decisions

  • Paraglide JS with its Vite plugin in the front only. Locales en (source, fallback) and fr.
  • Locale resolution order: stored choice in localStorage, then the browser language, then en. localStorage reads are guarded like the Seat token's.
  • Messages take parameters instead of concatenated fragments (Colors, Draw reasons, Offer kinds), so French can agree in gender and word order ("Les Blancs ont abandonné").
  • Notation (SAN, Room codes) is never translated.
  • The API keeps returning refusal codes only; translation stays in the front.
  • A language switch in the site header.
  • Needs a new ADR (front i18n with Paraglide, rejected: react-i18next, Lingui), written in its own PR per CLAUDE.md.

Testing Decisions

  • Prior art: the room view tests (room.test.tsx, room-presence.test.tsx) and the home view tests.
  • Unhappy paths: unsupported browser language falls back to English; a blocked localStorage still renders (browser language wins).
  • apps/e2e keeps asserting English strings; Playwright's default locale is English, so it stays green without changes.

Out of Scope

  • Any language other than English and French.
  • Locale in the URL (/fr/...).
  • Translating docs, ADRs or commit conventions.

Further Notes

The room rail spec rewrites much of the room copy; it should land after this one so its new strings go straight into the catalogue.

> _Imported from GitHub [amaurycoudr/chess-project#242](https://github.com/amaurycoudr/chess-project/issues/242), opened by [@amaurycoudr](https://github.com/amaurycoudr) on 2026-10-05 08:17 UTC._ > _Closed on GitHub on 2026-10-05 10:11 UTC as completed._ <!-- github-issue:242 --> --- ## Problem Statement Every word the app shows is hard-coded English, spread over constants in each vertical (status lines, refusals, confirmations, presence, offers, home). A French-speaking player cannot use the app in French, and changing any wording means hunting through components. ## Solution The front reads all its copy from one message catalogue, in English and French. The language follows the browser on first visit and can be switched from the site header; the choice is remembered on that device. The library is **Paraglide JS** (inlang): messages compile to typed functions, unused ones are tree-shaken, and it ships a Vite plugin. ## User Stories 1. As a player whose browser is in French, I want the app in French on my first visit, so that I don't have to look for a setting. 2. As a player whose browser is in any other language, I want the app in English, so that I always get a complete language. 3. As a player, I want to switch between English and French from the site header, so that I can override my browser. 4. As a player, I want my choice remembered on this device, so that I don't switch again on every visit. 5. As a player, I want every message translated — status lines, actions and their confirmations, offers, presence, toasts, refusals, home, local game — so that no English is left behind in French. 6. As a screen-reader user, I want the page's `lang` to match the language shown, so that it is read with the right voice. ## Seams - **Views through each vertical's harness and MSW** (view tests): the home, local and room views rendered with a given browser language and with a stored choice. Covers stories 1–6. ## Implementation Decisions - Paraglide JS with its Vite plugin in the front only. Locales `en` (source, fallback) and `fr`. - Locale resolution order: stored choice in `localStorage`, then the browser language, then `en`. `localStorage` reads are guarded like the Seat token's. - Messages take parameters instead of concatenated fragments (Colors, Draw reasons, Offer kinds), so French can agree in gender and word order ("Les Blancs ont abandonné"). - Notation (SAN, Room codes) is never translated. - The API keeps returning refusal codes only; translation stays in the front. - A language switch in the site header. - **Needs a new ADR** (front i18n with Paraglide, rejected: react-i18next, Lingui), written in its own PR per CLAUDE.md. ## Testing Decisions - Prior art: the room view tests (`room.test.tsx`, `room-presence.test.tsx`) and the home view tests. - Unhappy paths: unsupported browser language falls back to English; a blocked `localStorage` still renders (browser language wins). - `apps/e2e` keeps asserting English strings; Playwright's default locale is English, so it stays green without changes. ## Out of Scope - Any language other than English and French. - Locale in the URL (`/fr/...`). - Translating docs, ADRs or commit conventions. ## Further Notes The room rail spec rewrites much of the room copy; it should land after this one so its new strings go straight into the catalogue.
root commented 2026-10-05 08:17:34 +00:00 (Migrated from gitlab.core-project.space)

GitHub issue relations, imported as "related" links (GitLab CE has no sub-issues or blocking links).

> _GitHub issue relations, imported as "related" links (GitLab CE has no sub-issues or blocking links)._ <!-- github-relations --> - Blocking: #253, #246, #245
root (Migrated from gitlab.core-project.space) closed this issue 2026-10-05 10:11:15 +00:00
root commented 2026-10-13 08:39:13 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #243

mentioned in issue #243
root commented 2026-10-13 08:39:14 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #245

mentioned in issue #245
root commented 2026-10-13 08:39:14 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #246

mentioned in issue #246
root commented 2026-10-13 08:39:15 +00:00 (Migrated from gitlab.core-project.space)

mentioned in issue #253

mentioned in issue #253
root commented 2026-10-13 08:41:52 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #253

marked this issue as related to #253
root commented 2026-10-13 08:41:52 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #246

marked this issue as related to #246
root commented 2026-10-13 08:41:52 +00:00 (Migrated from gitlab.core-project.space)

marked this issue as related to #245

marked this issue as related to #245
root commented 2026-10-13 10:19:25 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 27277633ed

mentioned in commit 27277633ed8a5d981cfd8ade23fa1b907541a89b
root commented 2026-10-13 10:21:55 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 693508ae82

mentioned in commit 693508ae8288041b5859bb67a4ce829d78c38532
root commented 2026-10-13 10:21:55 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit a9c152f468

mentioned in commit a9c152f468d340c114b5c01738418f2d7eac8c9c
root commented 2026-10-13 10:21:55 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 730f97c5fb

mentioned in commit 730f97c5fbff3b43884b7cc711a743e49079d766
root commented 2026-10-13 10:21:55 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit d78c35c518

mentioned in commit d78c35c5180d351a2b22a06fac006e61289a4b78
root commented 2026-10-13 10:21:55 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit ebaeaa1026

mentioned in commit ebaeaa1026cac985c2749d48d6814b8b33be76b1
root commented 2026-10-13 10:21:56 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit ee94f74de8

mentioned in commit ee94f74de82fefb80a69dc7bb72520e6b0933daa
root commented 2026-10-13 10:21:56 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 5fbaf841f9

mentioned in commit 5fbaf841f978d360a0a58762b377a6859ba6a265
root commented 2026-10-13 10:21:56 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 4dabf1ec28

mentioned in commit 4dabf1ec2836d14c1d71908f33120012d088e985
root commented 2026-10-13 10:21:56 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 54b4189334

mentioned in commit 54b418933403a601eeecace1ade7ce93ea940697
root commented 2026-10-13 10:21:56 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 4363e6dac1

mentioned in commit 4363e6dac10907e7f7e6cb274a6ab0b705af3db7
root commented 2026-10-13 10:21:57 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 3a75fd0fce

mentioned in commit 3a75fd0fceb3d10e1897837fe7294e47da0b9779
root commented 2026-10-13 10:21:57 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 8d07b35dff

mentioned in commit 8d07b35dff299ab095fd8782a6e2c07ea7ab2d6a
root commented 2026-10-13 10:21:57 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 1f00ec7d74

mentioned in commit 1f00ec7d74987ec5700f4b947e9939cf64072380
root commented 2026-10-13 10:21:58 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit e79aa9701f

mentioned in commit e79aa9701f25540ba5387c7d465208301a225725
root commented 2026-10-13 10:21:58 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 92073ff33b

mentioned in commit 92073ff33b4539aa256a74fc53634390ccacebdf
root commented 2026-10-13 10:21:58 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 39fe1a5dbf

mentioned in commit 39fe1a5dbf3199ac7d226ba63c127467acfd8987
root commented 2026-10-13 10:21:58 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 31bd8aee7b

mentioned in commit 31bd8aee7beb0d45053ea6e9bf7b04624f9fb9bc
root commented 2026-10-13 10:21:58 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 1fc320e772

mentioned in commit 1fc320e772f2c090756780662189c70146cf0e3d
root commented 2026-10-13 10:21:59 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit 0ae546a572

mentioned in commit 0ae546a572f0ed466ffcb8ba625eca418e218f5f
root commented 2026-10-13 10:21:59 +00:00 (Migrated from gitlab.core-project.space)

mentioned in commit bb03e4a00b

mentioned in commit bb03e4a00ba0774918de16cbb42b80b5af0ceb0b
root commented 2026-10-19 19:12:15 +00:00 (Migrated from gitlab.core-project.space)

mentioned in merge request !557

mentioned in merge request !557
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
amaurycoudr/chess-project#242
No description provided.