feat(i18n): dynamic locale loading + Weblate setup (#71) #77

Merged
kreativmonkey merged 1 commit from feat/i18n-dynamic-locales into main 2026-06-14 22:26:44 +02:00

Implements the groundwork for the Weblate translation workflow requested in #71.

Why

Adding a language used to require editing three things: the JSON file, supportedLanguages in frontend/src/i18n/index.ts, and SupportedLanguages in internal/i18n/catalog.go. Those two hardcoded lists were the only thing preventing a no-code, Weblate-driven workflow.

Changes

  • Backend: SupportedLanguages is now derived from the embedded locale files via a new discoverLanguages(fs.FS) that globs locales/*.json (sorted; falls back to the default language if none exist).
  • Frontend: locales are loaded with import.meta.glob('../../../locales/*.json'); both the i18next resources and the language picker are built from that. Picker labels come from Intl.DisplayNames (the language's own name, e.g. "Deutsch").
  • Docs (docs/i18n.md): "adding a language" is now just dropping in a JSON file; added a Weblate section with the exact component config (file mask locales/*.json, monolingual base locales/en.json, format i18next JSON, source en) and the Forgejo webhook note.

After this, dropping a new locales/<code>.json into the repo is enough for it to be served by the API and appear in the UI picker — exactly what a Weblate component produces.

Tests

  • discoverLanguages unit tests (sorted, .json-only, empty-FS fallback) via fstest.MapFS.
  • SupportedLanguages is derived from the bundle (still includes en+de).
  • go test ./..., go vet, frontend pnpm lint + pnpm build all pass.

What still needs you (server-side)

Steps 1 and 3 are done in this PR. Step 2 — provisioning the actual Weblate instance, creating the component against this repo, and adding the Forgejo webhook — needs your Weblate server; the exact settings are in docs/i18n.md. If you give me the Weblate host URL I can create the Forgejo webhook via the API.

Refs #71.

🤖 Generated with Claude Code

Implements the groundwork for the Weblate translation workflow requested in #71. ## Why Adding a language used to require editing **three** things: the JSON file, `supportedLanguages` in `frontend/src/i18n/index.ts`, and `SupportedLanguages` in `internal/i18n/catalog.go`. Those two hardcoded lists were the only thing preventing a no-code, Weblate-driven workflow. ## Changes - **Backend:** `SupportedLanguages` is now derived from the embedded locale files via a new `discoverLanguages(fs.FS)` that globs `locales/*.json` (sorted; falls back to the default language if none exist). - **Frontend:** locales are loaded with `import.meta.glob('../../../locales/*.json')`; both the i18next `resources` and the language picker are built from that. Picker labels come from `Intl.DisplayNames` (the language's own name, e.g. "Deutsch"). - **Docs (`docs/i18n.md`):** "adding a language" is now just dropping in a JSON file; added a **Weblate** section with the exact component config (file mask `locales/*.json`, monolingual base `locales/en.json`, format `i18next JSON`, source `en`) and the Forgejo webhook note. After this, dropping a new `locales/<code>.json` into the repo is enough for it to be served by the API and appear in the UI picker — exactly what a Weblate component produces. ## Tests - `discoverLanguages` unit tests (sorted, `.json`-only, empty-FS fallback) via `fstest.MapFS`. - `SupportedLanguages` is derived from the bundle (still includes en+de). - `go test ./...`, `go vet`, frontend `pnpm lint` + `pnpm build` all pass. ## What still needs you (server-side) Steps 1 and 3 are done in this PR. Step 2 — provisioning the actual Weblate instance, creating the component against this repo, and adding the Forgejo webhook — needs your Weblate server; the exact settings are in `docs/i18n.md`. If you give me the Weblate host URL I can create the Forgejo webhook via the API. Refs #71. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(i18n): load locales dynamically so new languages need no code change
All checks were successful
CI/CD Workflow / Test Backend (Go) (pull_request) Successful in 3m1s
CI/CD Workflow / Test Frontend (Node.js) (pull_request) Successful in 1m25s
CI/CD Workflow / Test Postgres Store (pull_request) Successful in 1m21s
CI/CD Workflow / E2E (Playwright + Go) (pull_request) Successful in 6m46s
CI/CD Workflow / Build and Publish Docker Image (pull_request) Has been skipped
CI/CD Workflow / Trigger Dockhand Deployment (pull_request) Has been skipped
60c1db008b
Both the backend supported-language list and the web UI resource map were
hardcoded, so every new language required editing Go and TypeScript on top
of adding the translation file. This blocks a no-code translation workflow
(e.g. Weblate, issue #71).

- Backend: derive SupportedLanguages from the embedded locale files via a
  new discoverLanguages(fs.FS) that globs locales/*.json (sorted, falls back
  to the default language if none are found).
- Frontend: load every locales/*.json with import.meta.glob and build both
  the i18next resources and the language picker from it; labels come from
  Intl.DisplayNames (the language's own name).
- docs/i18n.md: adding a language is now just dropping in a JSON file; add a
  Weblate section (component config + Forgejo webhook).

Dropping a new locales/<code>.json into the repo is now enough for it to be
served by the API and appear in the UI picker.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
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.

Dependencies

No dependencies set.

Reference
kreativmonkey/goloom!77
No description provided.