Skip to content

feat(i18n): spec 019 language packs: catalog, checker, backend, module translations and designs - #668

Open
ValgulNecron wants to merge 44 commits into
masterfrom
claude/project-thread-awjqv0
Open

ValgulNecron wants to merge 44 commits into
masterfrom
claude/project-thread-awjqv0

Conversation

@ValgulNecron

@ValgulNecron ValgulNecron commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

Before: Gameplane has no translation system. Dashboard text, API errors, notifications and operator conditions are English-only, dates and numbers are hard-coded to en-US, and game modules can't ship translations.

After: Gameplane has a shared language catalog, a checker that runs in CI, and a backend that knows each user's language. The API saves each user's language and an instance default language. It returns localizable 4xx errors as RFC 9457 problem details to clients that ask for them. Notifications render in each destination's language, and the operator publishes condition message ids with their arguments. Modules can ship locales/*.json, which the operator publishes and the API serves. The dashboard has the i18n runtime and an en-XA pseudo-language for spotting untranslated text. The language picker and settings designs are in design.pen. Nothing visible changes in English yet, and plain-text API error bodies stay byte-identical.

This PR carries the cloud-built part of spec 019: 71 of 105 tasks in specs/019-localization-language-packs/tasks.md. The rest goes in follow-up PRs:

  • Dashboard UI built from the designs: T044, T045, T052–T060, T073, T089, T095, T100.
  • Hosted Weblate setup: T075–T081.
  • French translation: T096–T098. T035, T037, T062 and T092 are French end-to-end tests and wait on it.
  • Module repo: feat: module translations (spec 019 T090, T091) module#51 and the submodule pointer bump (T090, T091) after this merges, then T103–T105.

How

  • i18n/ (new Go module, coverage gate 90): icu is an ICU MessageFormat subset with CLDR plurals. pack loads and validates packs and applies the 80% listing rule. catalog holds the embedded packs and falls back regional → base → English. modlocale derives module translation ids. cmd/i18n-check checks extraction drift, server-message drift and module drift (--module), with one seeded defective fixture per finding code.
  • API:
    • internal/messages registry and httperr.WriteMessage. Problem details are returned only when Accept asks for application/problem+json (wildcards never select them), and 5xx responses never echo error text.
    • PUT /users/me/language, the instance default in /admin/config/i18n, and a per-sink language for notifications.
    • Serves module translations at /modules/{name}/locales/{tag}.
  • Operator:
    • Publishes status.conditionArgs and condition.* message ids.
    • Writes a module's translations to a <template>-locales ConfigMap in the operator namespace, with a LocalesValid condition (LocalesTooLarge, InvalidEntries, WriteFailed) and a 4 MiB total read cap.
  • gp-module: derives, validates, stamps (sourceHash) and packages module translations, and reports YAML names that cannot become translation ids.
  • Web: src/lib/i18n/ (provider, FR-004 resolution order, format helpers, server-message mapping), the user's and instance's language fed into the provider, invalid pack entries dropped at build time, and en-XA behind VITE_I18N_PSEUDO.
  • CI: the web job builds the manifest and runs i18n-check, and translation-only PRs are labelled docs. make check-i18n joins make lint.
  • Docs: translator guide, architecture docs and the changelog.

Test fixes that valgul approved on decision cards: the api-cache Accept header, the /auth/locale mock, the TestModulePacks fixture, the gp-module README fixture, the Migrate call in the preferences test, the validator canonical check, the escaped locale tag, the e2e lint fixes, the Template/Modèle fixture words and the AddressInUse envtest cleanup.

Only compile checks and the generators were run locally. Tests and lint run here in CI, and the latest head is green.

🤖 Generated with Claude Code

… and i18n-check

Spec 019 T001-T004, T007-T019: new Go module i18n/ (go.work, GO_MODULES,
CI matrices, dependabot, coverage gate 90), ICU MessageFormat subset
parser/formatter with CLDR plurals, pack load/validate/completeness,
embedded catalog with regional -> base -> en fallback, the i18n-check CLI
and one seeded defective fixture per finding code.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Spec 019 T005, T006 (web), T024-T033: react-intl provider with language
resolution, storage and no-flash boot snippet; locale-aware formatting
helpers replacing hard-coded en-US; problem+json parsing in APIError with
err.body kept as the English detail; extract/compile/manifest scripts;
formatjs ESLint rules on migrated paths. No visible UI change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Spec 019 T006 (api/operator), T020-T023: api/internal/messages registry
(server.* ids with translator notes), httperr.Message and WriteMessage,
RFC 9457 problem details only when Accept lists application/problem+json
with q > 0 (plain-text bodies stay byte-identical), 5xx never echo error
text or args, messages-export command and make i18n-sync writing the
server.* entries into i18n/locales/en/app.json.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Spec 019 T034, T048: the web job builds the language manifest, runs
i18n-check (extraction drift, server-message drift, before/after
completeness in the job summary) and self-tests the checker on the
seeded defective packs; make check-i18n joins make lint. i18n-check now
creates the manifest's directory.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
@ValgulNecron ValgulNecron added area: api REST/WebSocket gateway area: shared netguard, gameaction, gameproto, svcutil and other shared packages area: specs spec-kit feature specs and design docs area: web React dashboard type: feature New capability or user-facing addition labels Oct 11, 2026 — with Claude
@coderabbitai

coderabbitai Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 295 files, which is 195 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration
  • Configuration used: Repository: GameplanePanel/Gameplane/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e1386c2b-53ed-46b9-aa25-85f20425d822
📥 Commits

Reviewing files that changed from the base of the PR and between ec0f332 and 0107fde.

⛔ Files ignored due to path filters: 27
  • assets/design-export/screenshots/C2LQE4.png is excluded by !**/*.png
  • assets/design-export/screenshots/CDbjX.png is excluded by !**/*.png
  • assets/design-export/screenshots/EcoGD.png is excluded by !**/*.png
  • assets/design-export/screenshots/FBiMN.png is excluded by !**/*.png
  • assets/design-export/screenshots/G7OwqK.png is excluded by !**/*.png
  • assets/design-export/screenshots/HKEl2.png is excluded by !**/*.png
  • assets/design-export/screenshots/Jf27F.png is excluded by !**/*.png
  • assets/design-export/screenshots/N1GkB.png is excluded by !**/*.png
  • assets/design-export/screenshots/T1wkiT.png is excluded by !**/*.png
  • assets/design-export/screenshots/WZdnw.png is excluded by !**/*.png
  • assets/design-export/screenshots/eaNkj.png is excluded by !**/*.png
  • assets/design-export/screenshots/epZO2.png is excluded by !**/*.png
  • assets/design-export/screenshots/g2HLxz.png is excluded by !**/*.png
  • assets/design-export/screenshots/g5e2fM.png is excluded by !**/*.png
  • assets/design-export/screenshots/gX7um.png is excluded by !**/*.png
  • assets/design-export/screenshots/jmoi3.png is excluded by !**/*.png
  • assets/design-export/screenshots/lWvcv.png is excluded by !**/*.png
  • assets/design-export/screenshots/ljdA5.png is excluded by !**/*.png
  • assets/design-export/screenshots/n6Xlo.png is excluded by !**/*.png
  • assets/design-export/screenshots/q31B6w.png is excluded by !**/*.png
  • assets/design-export/screenshots/qFLfB.png is excluded by !**/*.png
  • design.pen is excluded by !**/*.pen
  • go.work is excluded by !**/*.work
  • gp-module/go.sum is excluded by !**/*.sum
  • i18n/go.sum is excluded by !**/*.sum
  • test/e2e/go.sum is excluded by !**/*.sum
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing: 295
  • .devcontainer/post-create.sh
  • .github/dependabot.yml
  • .github/workflows/ci.yaml
  • .github/workflows/publish-edge.yaml
  • .gitignore
  • CHANGELOG.md
  • CLAUDE.md
  • Makefile
  • README.md
  • api/Dockerfile
  • api/cmd/main.go
  • api/cmd/messages-export/main.go
  • api/docs/openapi.yaml
  • api/go.mod
  • api/internal/auth/local.go
  • api/internal/auth/local_test.go
  • api/internal/auth/oidc.go
  • api/internal/auth/ratelimit.go
  • api/internal/auth/ratelimit_test.go
  • api/internal/auth/sessions.go
  • api/internal/db/migrations/common/018_user_language.sql
  • api/internal/db/preferences.go
  • api/internal/db/preferences_test.go
  • api/internal/handlers/auth_provider_secret.go
  • api/internal/handlers/auth_providers.go
  • api/internal/handlers/auth_providers_test.go
  • api/internal/handlers/clusters.go
  • api/internal/handlers/config.go
  • api/internal/handlers/config_validators_test.go
  • api/internal/handlers/fleet.go
  • api/internal/handlers/lifecycle.go
  • api/internal/handlers/module_upload.go
  • api/internal/handlers/module_upload_locales_test.go
  • api/internal/handlers/modules.go
  • api/internal/handlers/modules_locales.go
  • api/internal/handlers/modules_locales_test.go
  • api/internal/handlers/notifications.go
  • api/internal/handlers/notifications_language_test.go
  • api/internal/handlers/ownership.go
  • api/internal/handlers/registry_secret.go
  • api/internal/handlers/resources.go
  • api/internal/handlers/roles.go
  • api/internal/handlers/shares.go
  • api/internal/handlers/telemetry.go
  • api/internal/handlers/tunnelcreds.go
  • api/internal/handlers/users.go
  • api/internal/handlers/users_language_test.go
  • api/internal/httperr/httperr.go
  • api/internal/httperr/httperr_test.go
  • api/internal/messages/entries_server.go
  • api/internal/messages/export.go
  • api/internal/messages/messages.go
  • api/internal/messages/messages_test.go
  • api/internal/messages/usage_test.go
  • api/internal/notify/format.go
  • api/internal/notify/language.go
  • api/internal/notify/language_test.go
  • api/internal/notify/messages.go
  • api/internal/notify/notify.go
  • api/internal/notify/sinks.go
  • api/internal/rbac/rbac.go
  • api/internal/ws/attach.go
  • api/internal/ws/podlogs.go
  • api/specs.md
  • assets/design-export/MANIFEST.md
  • assets/design-export/json/CDbjX.json
  • assets/design-export/json/G7OwqK.json
  • assets/design-export/json/HKEl2.json
  • assets/design-export/json/Jf27F.json
  • assets/design-export/json/N1GkB.json
  • assets/design-export/json/T1wkiT.json
  • assets/design-export/json/WZdnw.json
  • assets/design-export/json/eaNkj.json
  • assets/design-export/json/g2HLxz.json
  • assets/design-export/json/g5e2fM.json
  • assets/design-export/json/gX7um.json
  • assets/design-export/json/lWvcv.json
  • assets/design-export/json/ljdA5.json
  • assets/design-export/json/n6Xlo.json
  • charts/gameplane/crd-manifests/gameplane.local_backups.yaml
  • charts/gameplane/crd-manifests/gameplane.local_backupschedules.yaml
  • charts/gameplane/crd-manifests/gameplane.local_clusters.yaml
  • charts/gameplane/crd-manifests/gameplane.local_gameservers.yaml
  • charts/gameplane/crd-manifests/gameplane.local_gametemplates.yaml
  • charts/gameplane/crd-manifests/gameplane.local_modules.yaml
  • charts/gameplane/crd-manifests/gameplane.local_modulesources.yaml
  • charts/gameplane/crd-manifests/gameplane.local_networkcaptures.yaml
  • charts/gameplane/crd-manifests/gameplane.local_restores.yaml
  • charts/gameplane/crds/gameplane.local_backups.yaml
  • charts/gameplane/crds/gameplane.local_backupschedules.yaml
  • charts/gameplane/crds/gameplane.local_clusters.yaml
  • charts/gameplane/crds/gameplane.local_gameservers.yaml
  • charts/gameplane/crds/gameplane.local_gametemplates.yaml
  • charts/gameplane/crds/gameplane.local_modules.yaml
  • charts/gameplane/crds/gameplane.local_modulesources.yaml
  • charts/gameplane/crds/gameplane.local_networkcaptures.yaml
  • charts/gameplane/crds/gameplane.local_restores.yaml
  • docs/agent-architecture.md
  • docs/architecture.md
  • docs/contributing.md
  • docs/module-authoring.md
  • docs/notifications.md
  • docs/translating.md
  • gp-module/cmd/gp-module/i18n.go
  • gp-module/cmd/gp-module/i18n_stamp_test.go
  • gp-module/cmd/gp-module/i18n_test.go
  • gp-module/cmd/gp-module/main.go
  • gp-module/go.mod
  • gp-module/internal/i18n/ids.go
  • gp-module/internal/i18n/ids_test.go
  • gp-module/internal/i18n/stamp.go
  • gp-module/internal/i18n/stamp_test.go
  • gp-module/internal/packager/archive_locales_test.go
  • gp-module/internal/packager/locales_test.go
  • gp-module/internal/packager/packager.go
  • gp-module/internal/validator/locales.go
  • gp-module/internal/validator/locales_id_test.go
  • gp-module/internal/validator/locales_needs_review_test.go
  • gp-module/internal/validator/locales_test.go
  • gp-module/internal/validator/report.go
  • gp-module/internal/validator/validator.go
  • gp-module/specs.md
  • hack/auto_label.py
  • hack/test_auto_label.py
  • i18n/.testcoverage.yml
  • i18n/catalog/catalog.go
  • i18n/catalog/catalog_test.go
  • i18n/cmd/i18n-check/fixtures_test.go
  • i18n/cmd/i18n-check/main.go
  • i18n/cmd/i18n-check/main_test.go
  • i18n/cmd/i18n-check/module_ids_test.go
  • i18n/cmd/i18n-check/notify_registry_test.go
  • i18n/cmd/i18n-check/valid_test.go
  • i18n/doc.go
  • i18n/embed.go
  • i18n/go.mod
  • i18n/icu/format.go
  • i18n/icu/format_test.go
  • i18n/icu/parse.go
  • i18n/icu/parse_test.go
  • i18n/icu/placeholders.go
  • i18n/icu/placeholders_test.go
  • i18n/locales/en/app.json
  • i18n/locales/en/meta.json
  • i18n/modlocale/ids.go
  • i18n/modlocale/ids_test.go
  • i18n/modlocale/ids_yaml_parity_test.go
  • i18n/pack/completeness.go
  • i18n/pack/completeness_test.go
  • i18n/pack/pack.go
  • i18n/pack/pack_test.go
  • i18n/pack/stamp.go
  • i18n/pack/stamp_test.go
  • i18n/pack/validate.go
  • i18n/pack/validate_test.go
  • i18n/specs.md
  • i18n/testdata/defective/extra-placeholder/en/app.json
  • i18n/testdata/defective/extra-placeholder/xx/app.json
  • i18n/testdata/defective/extra-placeholder/xx/meta.json
  • i18n/testdata/defective/extract-drift/args
  • i18n/testdata/defective/extract-drift/en/app.json
  • i18n/testdata/defective/extract-drift/extracted.json
  • i18n/testdata/defective/extract-drift/xx/app.json
  • i18n/testdata/defective/extract-drift/xx/meta.json
  • i18n/testdata/defective/icu-syntax/en/app.json
  • i18n/testdata/defective/icu-syntax/xx/app.json
  • i18n/testdata/defective/icu-syntax/xx/meta.json
  • i18n/testdata/defective/invalid-meta/en/app.json
  • i18n/testdata/defective/invalid-meta/xx/app.json
  • i18n/testdata/defective/invalid-meta/xx/meta.json
  • i18n/testdata/defective/malformed-pack/en/app.json
  • i18n/testdata/defective/malformed-pack/xx/app.json
  • i18n/testdata/defective/malformed-pack/xx/meta.json
  • i18n/testdata/defective/markup/en/app.json
  • i18n/testdata/defective/markup/xx/app.json
  • i18n/testdata/defective/markup/xx/meta.json
  • i18n/testdata/defective/missing-description/en/app.json
  • i18n/testdata/defective/missing-description/xx/app.json
  • i18n/testdata/defective/missing-description/xx/meta.json
  • i18n/testdata/defective/missing-placeholder/en/app.json
  • i18n/testdata/defective/missing-placeholder/xx/app.json
  • i18n/testdata/defective/missing-placeholder/xx/meta.json
  • i18n/testdata/defective/needs-review/en/app.json
  • i18n/testdata/defective/needs-review/xx/app.json
  • i18n/testdata/defective/needs-review/xx/meta.json
  • i18n/testdata/defective/server-drift/args
  • i18n/testdata/defective/server-drift/en/app.json
  • i18n/testdata/defective/server-drift/registry.json
  • i18n/testdata/defective/server-drift/xx/app.json
  • i18n/testdata/defective/server-drift/xx/meta.json
  • i18n/testdata/defective/unknown-id/en/app.json
  • i18n/testdata/defective/unknown-id/xx/app.json
  • i18n/testdata/defective/unknown-id/xx/meta.json
  • i18n/testdata/defective/unused-id/args
  • i18n/testdata/defective/unused-id/en/app.json
  • i18n/testdata/defective/unused-id/used-ids.txt
  • i18n/testdata/defective/unused-id/xx/app.json
  • i18n/testdata/defective/unused-id/xx/meta.json
  • operator/Dockerfile
  • operator/api/v1alpha1/gameserver_types.go
  • operator/api/v1alpha1/gametemplate_types.go
  • operator/api/v1alpha1/zz_generated.deepcopy.go
  • operator/cmd/main.go
  • operator/config/crd/gameplane.local_gameservers.yaml
  • operator/config/crd/gameplane.local_gametemplates.yaml
  • operator/config/rbac/role.yaml
  • operator/go.mod
  • operator/hack/condition-ids/main.go
  • operator/internal/controller/gameserver_condition_args.go
  • operator/internal/controller/gameserver_condition_args_envtest_test.go
  • operator/internal/controller/gameserver_controller.go
  • operator/internal/controller/gameserver_status.go
  • operator/internal/controller/gameserver_tunnel.go
  • operator/internal/controller/module_controller.go
  • operator/internal/controller/module_locales.go
  • operator/internal/controller/module_locales_envtest_test.go
  • operator/internal/controller/module_locales_review_test.go
  • operator/internal/controller/module_locales_test.go
  • operator/internal/modsrc/bundle.go
  • operator/internal/modsrc/bundle_locales_test.go
  • operator/internal/modsrc/dir.go
  • operator/internal/modsrc/dir_locales_budget_test.go
  • operator/internal/modsrc/dir_locales_capped_test.go
  • operator/internal/modsrc/dir_locales_test.go
  • operator/internal/modsrc/http_locales_test.go
  • operator/internal/modsrc/upload.go
  • operator/internal/modsrc/upload_locales_test.go
  • operator/internal/oci/bundle.go
  • operator/internal/oci/client.go
  • operator/internal/oci/locale_budget_test.go
  • operator/internal/oci/locale_test.go
  • operator/specs.md
  • specs/019-localization-language-packs/OPEN-DECISIONS.md
  • specs/019-localization-language-packs/contracts/check-tool.md
  • specs/019-localization-language-packs/contracts/module-locales.md
  • specs/019-localization-language-packs/contracts/pack-format.md
  • specs/019-localization-language-packs/data-model.md
  • specs/019-localization-language-packs/plan.md
  • specs/019-localization-language-packs/research.md
  • specs/019-localization-language-packs/spec.md
  • specs/019-localization-language-packs/tasks.md
  • specs/done_010-easy-module-building/contracts/diagnostics-contract.md
  • test/e2e/buckets.sh
  • test/e2e/fixtures/oras-push-i18n-bad.yaml
  • test/e2e/fixtures/oras-push-i18n-fr.yaml
  • test/e2e/go.mod
  • test/e2e/i18n_auth_locale_e2e_test.go
  • test/e2e/i18n_module_locales_e2e_test.go
  • test/e2e/i18n_problem_json_e2e_test.go
  • web/Dockerfile
  • web/Dockerfile.dockerignore
  • web/e2e/specs/api-cache.spec.ts
  • web/eslint.config.js
  • web/index.html
  • web/package.json
  • web/scripts/i18n-compile.mjs
  • web/scripts/i18n-fixtures.mjs
  • web/scripts/i18n-pseudo-lib.d.mts
  • web/scripts/i18n-pseudo-lib.mjs
  • web/scripts/i18n-pseudo.mjs
  • web/scripts/i18n-reference.mjs
  • web/specs.md
  • web/src/__tests__/i18nBoot.test.ts
  • web/src/components/CaptureWidget.tsx
  • web/src/components/ui/NotificationsPanel.tsx
  • web/src/lib/api.test.ts
  • web/src/lib/api.ts
  • web/src/lib/endpoints.ts
  • web/src/lib/endpoints_modules_locales.test.ts
  • web/src/lib/endpoints_more.test.ts
  • web/src/lib/errors.ts
  • web/src/lib/i18n/AppI18nProvider.test.tsx
  • web/src/lib/i18n/I18nProvider.test.tsx
  • web/src/lib/i18n/I18nProvider.tsx
  • web/src/lib/i18n/format.test.tsx
  • web/src/lib/i18n/format.ts
  • web/src/lib/i18n/languages.test.ts
  • web/src/lib/i18n/languages.ts
  • web/src/lib/i18n/pseudo.test.ts
  • web/src/lib/i18n/resolve.test.ts
  • web/src/lib/i18n/resolve.ts
  • web/src/lib/i18n/serverMessage.test.tsx
  • web/src/lib/i18n/serverMessage.ts
  • web/src/lib/i18n/storage.test.ts
  • web/src/lib/i18n/storage.ts
  • web/src/main.tsx
  • web/src/routes/AdminSettings.tsx
  • web/src/routes/ThemeSettings.tsx
  • web/src/routes/tabs/Logs.tsx
  • web/src/routes/tabs/settings/NetworkCapture.tsx
  • web/src/routes/tabs/settings/ShareLinks.tsx
  • web/src/test/handlers.ts
  • web/src/types.ts
  • web/vite.config.ts
  • web/vitest.config.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ValgulNecron ValgulNecron self-assigned this Oct 11, 2026
…tions

golangci-lint (gosec G703, staticcheck QF1001) on the new i18n module.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
eslint formatjs/enforce-description on src/lib/i18n/serverMessage.ts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

CI Report - 0107fde

run 38640625972 - attempt 1

0 failed - 32 passed - 0 skipped - 70m34s elapsed

Coverage

Module Measured Gate Margin
web L95.59 S94.08 B89.16 F91.61 L92 S92 B82 F76 +2.08

Changed

Area Files +/-
i18n/ 75 +6038/-0
api/ 55 +3423/-211
operator/ 34 +3111/-40
web/ 47 +2888/-103
(root) 7 +2521/-119
assets/ 36 +2197/-114
gp-module/ 20 +1283/-8
test/ 8 +708/-8
specs/ 10 +221/-87
docs/ 6 +276/-2
charts/ 18 +164/-18
.github/ 3 +74/-2
hack/ 2 +53/-4
.devcontainer/ 1 +1/-1

Slowest jobs

Job Duration
e2e telemetry / arm64 (kind) 23m42s
e2e telemetry / amd64 (kind) 22m42s
e2e capture overhead (kind) 13m48s
build e2e images 13m23s
e2e multicluster / arm64 (kind x2) 11m26s

e2e buckets run

api-agent, api-auth, api-mods, api-rbac, api-roles, bot-fast, multicluster, operator, ratelimit, telemetry, upgrade

Copy link
Copy Markdown
Collaborator Author

web e2e (mock) fails in web/e2e/specs/api-cache.spec.ts:68. The test expects API reads to send exactly Accept: application/json, but this PR sends Accept: application/json, application/problem+json, which spec 019 contracts/server-messages.md §1 requires. The test exists to check that API reads never ask for HTML, and that still holds. Only the exact header string changed. Updating the test's expected value needs the maintainer's sign-off, which has been asked for in the project thread. Until then this check stays red.


Generated by Claude Code

claude added 12 commits October 12, 2026 00:03
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
…guage

Spec 019 T036, T038-T041, T093: migration 016 adds user_preferences.language;
PUT /users/me/language (listed languages only, server.i18n.unknown_language);
language in GET /users/me/preferences; unauthenticated GET /auth/locale
exposing only defaultLanguage; admin config section i18n validated against
the listed languages (server.i18n.unlisted_language). E2E for /auth/locale
in the api-auth bucket.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
…ider

Spec 019 T042, T046, T047: Users.setLanguage and Auth.locale endpoints,
AppI18nProvider reading the cached /users/me language and /auth/locale
default, and the 0% fixture language behind VITE_I18N_FIXTURES=1.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

GameServerStatus gains conditionArgs (map of condition type to the values
interpolated into its English message), set after each status pass and
cleared when the reason carries no values. The condition-ids generator
writes condition.<type>.<reason> English entries into the reference pack
and has a -check mode for drift.

Signed-off-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Sinks take an optional language (validated against the listed languages).
Titles, details and test text come from notify.* catalog entries in the
sink language, then the instance default, then English. Webhook payloads
add language, title and text and keep every existing field. English output
is unchanged.

Signed-off-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

Every 4xx http.Error in handlers, auth, ws and rbac now goes through
httperr.WriteMessage (registry id) or httperr.WriteCode (generic id).
Plain-text bodies stay byte-identical; 5xx stays http.Error. Unauthenticated
login and OIDC paths only use server.auth.invalid_credentials or generic
ids. make i18n-sync also syncs notify.* and condition.* ids, and
i18n-check covers notify.* drift. Adds the problem+json E2E and ticks the
019 US5 backend tasks.

Signed-off-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

i18n-check gains --valid-out, which writes each pack's usable entries.
The compile script and the web image use it, so a broken translation
shows the English text instead (FR-018).

Signed-off-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
valgul approved this expectation change on the spec 019 decision card.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

New i18n/modlocale derives module ids from module.yaml and template.yaml
with reversible ~0/~1 escaping. gp-module gains i18n extract and i18n
stamp, validate checks locales/*.json, and package adds one OCI layer per
language and includes locales/ in --output tarballs. i18n-check --module
checks en.json drift and validates translations.

Signed-off-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

valgul ruled on OD-007 (report in validate), OD-009 (WriteFailed ranks
first) and OD-013 (i18n-check --module --stamp stays an error).

Signed-off-by: Claude <noreply@anthropic.com>
ValgulNecron pushed a commit to GameplanePanel/module that referenced this pull request Oct 12, 2026
The locale check needs GameplanePanel/Gameplane#668 on master; running
it last keeps the build.sh and validate.py tests visible meanwhile.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
ValgulNecron and others added 2 commits October 12, 2026 21:55
Design for T043, T072 and T094 of spec 019, with exports.

- Personal settings: a Language card is the first card of Theme &
  Appearance (also on the Warnings variant), plus a mobile screen.
- Login and share pages: a compact picker in the top-right corner of
  the card on every state, and a screen with its menu open. It shows
  language names only.
- Notifications: a Language select on each sink row and a new screen
  for the Add sink form, which the design did not have.
- Admin settings: a Language card with the Default language field.

Records the placement rulings as OD-015 to OD-017 and ticks the three
design tasks. loginCard (J14ME) is unchanged: Pencil cannot insert a
child into it, so the login screens use a wrapper component (see
assets/design-export/MANIFEST.md).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
PR #668 conflicted with master, so no CI run started. Four conflicts,
one hunk each, resolved by keeping both sides:

- Makefile: lint runs check-i18n and check-coverage-gaps.
- api/go.mod: golang.org/x/sys v0.49.0 from master; golang.org/x/text
  stays a direct requirement.
- web/package.json and web/package-lock.json: the @formatjs dev
  dependencies from the branch, @playwright/test ^1.64.0 from master.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>

Copy link
Copy Markdown
Collaborator Author

web e2e (mock) fails on c0f0aba in two tests: serverDetail.spec.ts:35 and settingsSubTabs.spec.ts:50. Both assert that no console errors appear, and both see Failed to load resource: ... 502 (Bad Gateway).

Cause: this PR's AppI18nProvider requests GET /auth/locale. The browser MSW handlers in web/src/test/handlers.ts have no route for it, so the request falls through to the dev proxy, which has no backend and returns 502. The fix is a one-line mock route in the default and screenshot handler sets. That changes test setup, so it waits for the maintainer's sign-off. No assertion changes.


Generated by Claude Code

…lgul)

The dashboard asks for the instance default language on every page load.
Without a mock route the request reached the dev proxy and returned 502,
which failed the console-error checks in serverDetail.spec.ts and
settingsSubTabs.spec.ts.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
…approved by valgul)

--module now checks en.json against the ids the template derives (OD Q4),
so the fixture template declares spec.displayName and the packs use the
derived id template.displayName. Both cases and exit codes are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
hadolint DL3062 flags go run without a version; building the local
package and running the binary does the same work.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
…ad-awjqv0

Signed-off-by: Claude <noreply@anthropic.com>
Codegen drift gate (make tidy) expects the tidied i18n/go.sum.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

docs and repository checks fails on 45c29e4 at make check-coverage-gaps. The error is specs/013-expand-test-coverage/coverage-gaps.md:3: Summary closed count is 3, table has 4 (and open 18 vs 17).

This isn't caused by this PR: it doesn't touch that file, and master 2155568 has the same mismatch. #672 and #674 each flipped a gap against the same old summary line. No fix exists yet. I've asked the spec 013 thread, which owns the file, to fix it on master. I'll merge master here once the fix lands.


Generated by Claude Code

golangci-lint: errcheck on the i18n command's output writes, gosec G602
on the --output value read, and G703 on the stamp write. Translations
are now written through an os.Root on the locales directory.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
gosec G703 on the condition-ids generator's write of app.json.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

web is red on b1b2508 because CaptureWidget.test.tsx › "enables download button only for Completed captures" failed at line 798.

The test races the capabilities query. It asserts the Completed row's download button is enabled right after the list renders, but canDownload stays false until capabilities resolves. This PR's only change to CaptureWidget.tsx swaps toLocaleString() for fmt.number(), which doesn't touch the disable logic. The same test passed on this PR's earlier runs.

A fix already exists on #677 (a8ffe53, which wraps the assertion in waitFor). It isn't ported here because this repo needs explicit sign-off before any test edit. It will reach this PR when #677 merges and master is merged in.


Generated by Claude Code

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
- gp-module: locale archive fixture includes the required README.md
- api/db: theme migration test applies the remaining migrations before
  reading preferences (016 adds language)
- api/handlers: TestValidateI18n checks the canonical JSON; the locales
  test path-escapes its invalid tag so the request reaches the handler
- e2e: close the locales response body; drop two unused ct assignments

Each change approved by valgul on its decision card.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
misspell flags the French "Exemple" as a typo of "Example". The
fixture pair becomes Template/Modèle; the test still compares message
ids only. Approved by valgul.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

go (operator) is red on both architectures because TestGameServer_ReleasedAddressBecomesAvailable times out (gameserver_envtest_test.go:3805).

The cause is in this PR. The new TestGameServer_ConditionArgsAddressInUse holds 203.0.113.20, the same address the existing test uses. Address conflicts are checked cluster-wide, and envtest never deletes namespaces, so the leftover holder keeps the other test reporting AddressInUse.

Fixing it means editing the new test. This repo needs explicit sign-off for test edits, and one option (a different IP) was declined. The fix is waiting on a decision in the project thread.


Generated by Claude Code

The holder kept 203.0.113.20 after the test, and address conflicts are
cluster-wide, so TestGameServer_ReleasedAddressBecomesAvailable saw the
address as still in use. Both servers are now deleted on cleanup.
Approved by valgul.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

e2e operator / amd64 (kind) failed on 53794a3 in TestGameServer_NetworkCaptureEphemeralContainer (gameserver_e2e_test.go:989: status.capture.ready still false after 1m30s).

I don't think this PR caused it:

  • status.capture.ready is set only from the capture ephemeral container's running state (operator/internal/controller/gameserver_controller.go:1838). This PR doesn't change that code or the capture sidecar. Its only change to the NetworkCapture CRD is the bundle hash annotation.
  • The other capture e2e tests passed in the same run (filter, no-filter, concurrency, pod restart).
  • e2e operator passed on the previous head 2ec9b39, which had the same operator changes. The only change since then is the master merge (PR ci: container image scan gate with Trivy (spec 013 G2) #678, CI/deps), and master's CI on d7fa8c1 is green.

No fix exists for this yet. I'll re-run the failed job once, after the rest of the run finishes, because GitHub won't re-run a job while its workflow is still running. If it fails a second time, I'll treat it as real.


Generated by Claude Code

@ValgulNecron ValgulNecron changed the title feat(i18n): spec 019 foundation: language packs, checker, problem+json, dashboard runtime feat(i18n): spec 019 language packs: catalog, checker, backend, module translations and designs Oct 14, 2026
@ValgulNecron
ValgulNecron marked this pull request as ready for review October 14, 2026 17:00
@ValgulNecron ValgulNecron added area: operator controller-runtime operator and CRDs area: chart Helm chart and deployment area: e2e kind-based end-to-end suite labels Oct 14, 2026 — with Claude
Brings in PR #685. assets/design-export/MANIFEST.md conflicted; both
sections are kept (spec 019 pickers 2026-10-12, then the PR #685
banner export 2026-10-14).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Brings in PRs #630, #690, #691 and #696. Conflicts resolved:
- api/internal/notify/format.go: master's remote-cluster headline is
  kept through a new notify.title.cluster message, so
  "[prod] server unhealthy: remote-1/games/mc" is unchanged in English;
  app.json regenerated with make i18n-sync.
- web/src/lib/endpoints.ts: master's cluster parameter on Modules,
  and Modules.locales takes the same parameter.
- web/src/components/ui/NotificationsPanel.tsx: both the formatter ref
  effect and master's enabled guard.
- docs/architecture.md: master's standalone-panel paragraph, then the
  Internationalization section.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Master added 016_standalone_management.sql and 017_standalone_key_state.sql,
so 016_user_language.sql collided on version 016 and Migrate refused to
run. The language column moves to the next free version; spec 019 docs
and api/specs.md follow.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: api REST/WebSocket gateway area: chart Helm chart and deployment area: e2e kind-based end-to-end suite area: operator controller-runtime operator and CRDs area: shared netguard, gameaction, gameproto, svcutil and other shared packages area: specs spec-kit feature specs and design docs area: web React dashboard type: feature New capability or user-facing addition

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants