Skip to content

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

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

ValgulNecron wants to merge 65 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 dashboard UI built from the designs (T044, T045, T052–T060, T073, T089, T095, T100) is being built locally and lands on this same PR. Until that is pushed and CI is green, the PR stays a draft. The rest goes in follow-up PRs:

  • 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}.
    • The user language column is migration 018_user_language.sql, because master already uses 016 and 017.
  • 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.

🤖 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 445 files, which is 345 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.

Usage-priced reviews support at most 300 files.

⚙️ Run configuration
  • Configuration used: Repository: GameplanePanel/Gameplane/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3c9fd005-39db-4c56-831f-ceea5f3b04b6
📥 Commits

Reviewing files that changed from the base of the PR and between 4b11c41 and a009569.

⛔ Files ignored due to path filters: 30
  • assets/design-export/screenshots/C2LQE4.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/GsofJ.png is excluded by !**/*.png
  • assets/design-export/screenshots/HKEl2.png is excluded by !**/*.png
  • assets/design-export/screenshots/Ii9Hy.png is excluded by !**/*.png
  • assets/design-export/screenshots/N1GkB.png is excluded by !**/*.png
  • assets/design-export/screenshots/S0P4bl.png is excluded by !**/*.png
  • assets/design-export/screenshots/SUcad.png is excluded by !**/*.png
  • assets/design-export/screenshots/T1wkiT.png is excluded by !**/*.png
  • assets/design-export/screenshots/TSXYO.png is excluded by !**/*.png
  • assets/design-export/screenshots/WZdnw.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/gX7um.png is excluded by !**/*.png
  • assets/design-export/screenshots/jmoi3.png is excluded by !**/*.png
  • assets/design-export/screenshots/lCTci.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
  • assets/design-export/screenshots/vp8BJ.png is excluded by !**/*.png
  • assets/design-export/screenshots/z3mtgc.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: 445
  • .devcontainer/post-create.sh
  • .github/dependabot.yml
  • .github/workflows/ci.yaml
  • .github/workflows/publish-edge.yaml
  • .github/workflows/visual-diff.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/C2LQE4.json
  • assets/design-export/json/D9QYZ.json
  • assets/design-export/json/DBQyF.json
  • assets/design-export/json/EcoGD.json
  • assets/design-export/json/FBiMN.json
  • assets/design-export/json/FOVMf.json
  • assets/design-export/json/GsofJ.json
  • assets/design-export/json/HKEl2.json
  • assets/design-export/json/Ii9Hy.json
  • assets/design-export/json/K5XinZ.json
  • assets/design-export/json/M2sA4u.json
  • assets/design-export/json/N1GkB.json
  • assets/design-export/json/Ne5TA.json
  • assets/design-export/json/QgW58.json
  • assets/design-export/json/RC3Kf.json
  • assets/design-export/json/S0P4bl.json
  • assets/design-export/json/SUcad.json
  • assets/design-export/json/T1wkiT.json
  • assets/design-export/json/TSXYO.json
  • assets/design-export/json/Uzzje.json
  • assets/design-export/json/WZdnw.json
  • assets/design-export/json/Wj0V4.json
  • assets/design-export/json/cDiNb.json
  • assets/design-export/json/epZO2.json
  • assets/design-export/json/g2HLxz.json
  • assets/design-export/json/g5mEpx.json
  • assets/design-export/json/gX7um.json
  • assets/design-export/json/l64bd.json
  • assets/design-export/json/l7TRz9.json
  • assets/design-export/json/lCTci.json
  • assets/design-export/json/lWvcv.json
  • assets/design-export/json/n6Xlo.json
  • assets/design-export/json/nNGDX.json
  • assets/design-export/json/q31B6w.json
  • assets/design-export/json/qFLfB.json
  • assets/design-export/json/r0heh.json
  • assets/design-export/json/uMiwd.json
  • assets/design-export/json/uoxQW.json
  • assets/design-export/json/vp8BJ.json
  • assets/design-export/json/yrxK2.json
  • assets/design-export/json/z3mtgc.json
  • assets/design-export/json/zM0VF.json
  • assets/design-export/json/zqzr4.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/screenshots/capture.ts
  • web/e2e/specs/api-cache.spec.ts
  • web/eslint.config.js
  • web/index.html
  • web/package.json
  • web/playwright.config.ts
  • web/scripts/i18n-compile.mjs
  • web/scripts/i18n-fixtures.mjs
  • web/scripts/i18n-migrate.ts
  • 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/AppLayout.tsx
  • web/src/components/CaptureWidget.tsx
  • web/src/components/ClusterSelector.tsx
  • web/src/components/FleetScope.tsx
  • web/src/components/ModuleTarget.tsx
  • web/src/components/RegisterClusterDialog.tsx
  • web/src/components/RequireRole.tsx
  • web/src/components/backups/BackupDetailDrawer.tsx
  • web/src/components/backups/BackupFilters.tsx
  • web/src/components/backups/BackupRow.tsx
  • web/src/components/backups/RestoreDialog.tsx
  • web/src/components/backups/RetentionFields.tsx
  • web/src/components/backups/ScheduleForm.tsx
  • web/src/components/modules/BuildModuleDialog.tsx
  • web/src/components/modules/InstallDialog.tsx
  • web/src/components/modules/ModuleCard.tsx
  • web/src/components/modules/ModuleSourcesPanel.tsx
  • web/src/components/modules/SourceDialog.tsx
  • web/src/components/modules/UploadModuleDialog.tsx
  • web/src/components/registry-browser.tsx
  • web/src/components/server/CloneServerDialog.tsx
  • web/src/components/server/ConfigFields.tsx
  • web/src/components/server/ConfigFields_moduleText.test.tsx
  • web/src/components/server/DeleteServerDialog.tsx
  • web/src/components/server/EventList.tsx
  • web/src/components/server/PortOverridesEditor.tsx
  • web/src/components/server/ServerActionsCard.tsx
  • web/src/components/server/ServerActionsMenu.tsx
  • web/src/components/server/ServerSleepCard.tsx
  • web/src/components/server/ServerStatusCard.tsx
  • web/src/components/server/TransferServerDialog.tsx
  • web/src/components/server/WipeServerDialog.tsx
  • web/src/components/settings/CompactLanguagePicker.tsx
  • web/src/components/settings/LanguagePicker.test.tsx
  • web/src/components/settings/LanguagePicker.tsx
  • web/src/components/ui/AppLoadingSkeleton.tsx
  • web/src/components/ui/AppearanceToggle.tsx
  • web/src/components/ui/AuditIntegrityBanner.tsx
  • web/src/components/ui/Breadcrumbs.tsx
  • web/src/components/ui/CaptureWarningBanner.tsx
  • web/src/components/ui/ConfirmAdminMappingDialog.tsx
  • web/src/components/ui/ConfirmDialog.tsx
  • web/src/components/ui/DropdownMenu.tsx
  • web/src/components/ui/ErrorBanner.tsx
  • web/src/components/ui/ErrorCard.tsx
  • web/src/components/ui/FilterPopover.tsx
  • web/src/components/ui/GlobalSearch.tsx
  • web/src/components/ui/LoadingCard.tsx
  • web/src/components/ui/Meter.tsx
  • web/src/components/ui/NotificationsPanel.tsx
  • web/src/components/ui/PhaseChip.tsx
  • web/src/components/ui/ProvenanceBadge.tsx
  • web/src/components/ui/RemovableGroupChip.tsx
  • web/src/components/ui/ResourceInput.tsx
  • web/src/components/ui/RoleEditorModal.tsx
  • web/src/components/ui/SafeModeBanner.tsx
  • web/src/components/ui/SettingsNav.tsx
  • web/src/components/ui/Sidebar.tsx
  • web/src/components/ui/TelemetryNotice.tsx
  • web/src/components/ui/TopBar.tsx
  • web/src/components/ui/admin/EditUserDialog.tsx
  • web/src/components/ui/admin/InviteUserDialog.tsx
  • web/src/components/ui/admin/ResetPasswordDialog.tsx
  • web/src/lib/api.test.ts
  • web/src/lib/api.ts
  • web/src/lib/config.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/fleet.ts
  • web/src/lib/games.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/I18nProvider_pseudo.test.tsx
  • web/src/lib/i18n/conditionMessage.test.ts
  • web/src/lib/i18n/conditionMessage.ts
  • 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/moduleText.test.tsx
  • web/src/lib/i18n/moduleText.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/lib/settingsMerge.ts
  • web/src/lib/theme-export.ts
  • web/src/lib/useServerCapabilities.ts
  • web/src/lib/useThemePreferences.ts
  • web/src/lib/utils.ts
  • web/src/lib/validation.ts
  • web/src/main.tsx
  • web/src/routes/AdminLogs.tsx
  • web/src/routes/AdminSettings.test.tsx
  • web/src/routes/AdminSettings.tsx
  • web/src/routes/AdminSettings_notifications.test.tsx
  • web/src/routes/AdminSettings_sections.test.tsx
  • web/src/routes/AuditLog.tsx
  • web/src/routes/Backups.tsx
  • web/src/routes/Cluster.tsx
  • web/src/routes/Clusters.tsx
  • web/src/routes/CreateServer.tsx
  • web/src/routes/Dashboard.tsx
  • web/src/routes/Login.tsx
  • web/src/routes/Login_language.test.tsx
  • web/src/routes/Modules.tsx
  • web/src/routes/NotFound.tsx
  • web/src/routes/ServerDetail.tsx
  • web/src/routes/Servers.tsx
  • web/src/routes/Share.tsx
  • web/src/routes/Share_language.test.tsx
  • web/src/routes/ThemeSettings.tsx
  • web/src/routes/ThemeSettings_language.test.tsx
  • web/src/routes/Users.tsx
  • web/src/routes/tabs/Backups.tsx
  • web/src/routes/tabs/Console.tsx
  • web/src/routes/tabs/ConsoleShell.tsx
  • web/src/routes/tabs/Events.tsx
  • web/src/routes/tabs/Files.tsx
  • web/src/routes/tabs/Logs.tsx
  • web/src/routes/tabs/Modpacks.tsx
  • web/src/routes/tabs/Mods.tsx
  • web/src/routes/tabs/Overview.tsx
  • web/src/routes/tabs/Players.tsx
  • web/src/routes/tabs/Settings.tsx
  • web/src/routes/tabs/settings/Access.tsx
  • web/src/routes/tabs/settings/Backups.tsx
  • web/src/routes/tabs/settings/Danger.tsx
  • web/src/routes/tabs/settings/EnvVars.tsx
  • web/src/routes/tabs/settings/GameConfig.tsx
  • web/src/routes/tabs/settings/General.tsx
  • web/src/routes/tabs/settings/Lifecycle.tsx
  • web/src/routes/tabs/settings/NetworkCapture.tsx
  • web/src/routes/tabs/settings/Networking.tsx
  • web/src/routes/tabs/settings/Placement.tsx
  • web/src/routes/tabs/settings/Resources.tsx
  • web/src/routes/tabs/settings/ShareLinks.tsx
  • web/src/routes/tabs/settings/Version.tsx
  • web/src/routes/tabs/settings/types.ts
  • 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 - a009569

run 38831683575 - attempt 1

0 failed - 32 passed - 0 skipped - 36m3s elapsed

Coverage

Module Measured Gate Margin
web L95.85 S94.36 B89.29 F91.76 L92 S92 B82 F76 +2.36

Changed

Area Files +/-
i18n/ 75 +15650/-0
web/ 167 +9673/-3890
assets/ 68 +4143/-142
api/ 55 +3423/-211
operator/ 34 +3126/-40
(root) 7 +2074/-163
gp-module/ 20 +1283/-8
test/ 8 +708/-8
specs/ 10 +254/-102
docs/ 6 +338/-2
charts/ 18 +164/-18
.github/ 4 +129/-2
hack/ 2 +53/-4
.devcontainer/ 1 +1/-1

Slowest jobs

Job Duration
e2e telemetry / arm64 (kind) 23m44s
e2e telemetry / amd64 (kind) 23m0s
e2e capture overhead (kind) 13m6s
build e2e images 12m38s
e2e multicluster / amd64 (kind x2) 11m44s

e2e buckets run

api-agent, api-auth, api-mods, api-rbac, api-roles, bot-fast, multicluster, operator, optional-components, 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>
@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>
@ValgulNecron
ValgulNecron marked this pull request as draft October 15, 2026 17:06
Brings in PRs #618, #621, #648, #693, #697-#700 and #702. PR #702
regrouped design.pen. valgul chose to take master's design.pen and
design exports, so the branch's spec 019 picker designs and their
exports are dropped and T043, T072 and T094 are reopened, to be
redrawn on the regrouped canvas in Pencil.

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

design vs browser visual diff fails on 4e35ff8. One screen is over its limit: DWztv (Mobile — Servers, Light) at 4.11% global diff, where the maximum is 4.00%.

This failure comes from master, not this PR:

I couldn't find an open PR that fixes it, so I haven't ported anything into this PR. The fix belongs on master: either re-export DWztv or adjust the mobile Servers screen. I'm not re-running the check, because master gives the same result.


Generated by Claude Code

ValgulNecron and others added 20 commits October 15, 2026 21:20
Adds web/scripts/i18n-migrate.ts (npm run i18n:migrate), run with Node's
type stripping and the installed TypeScript compiler API only. It moves
JSX text and display props (aria-label, title, placeholder, alt, and
label/hint/subtitle/message-like props on components) to
intl.formatMessage with ids web.<path>.<slug> and a description stub,
inserts useOptionalIntl() at the top of the enclosing component or hook,
keeps sentences with simple computed values as one ICU message, checks
every message parses and renders the same English, is idempotent, and
reports what needs a human (ternaries, rich text, JSX outside components).

i18n-reference.mjs now writes the merged reference to
i18n/locales/en/app.json, as `npm run i18n:extract` is documented to do;
it used to overwrite src/generated/i18n/extracted.json instead, so the
reference CI compares against never received web.* entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
Redraws the spec 019 designs (T043, T072, T094) on the regrouped
design.pen, after the 2026-10-12 pass was dropped when master's canvas
regroup (PR #702) was taken in the merge.

- Personal settings: a Language card (component Gameplane/Language Card)
  is the first card of Theme & Appearance and its Warnings variant, plus
  a mobile screen. Native names with completeness, an "Automatic
  (browser language)" option and a note that missing text shows in
  English (OD-016).
- Login and share pages: a compact picker (component Gameplane/Language
  Picker Compact) in the top-right corner of every card, and a login
  screen with its menu open. It shows language names only (OD-017).
- Notifications: a Language select on each sink row (component
  Gameplane/Sink Language Row) and a new Add sink screen whose Language
  select defaults to Instance default (OD-015).
- Admin settings: a Language card under General with the Default
  language field and its own Save changes button (config section i18n).

Exports: JSON and 2x PNG for every new or changed node, each JSON checked
against the canvas by length and FNV-1a hash; root x/y patched for the
screens the row shifts moved. Details in 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>
Adds a --check mode that reproduces formatjs/no-literal-string-in-jsx
locally (JSX text, string and template literals in child expressions
including ternary, logical and + branches, and the aria-*/title/
placeholder/alt props), since ESLint is not run locally. The codemod
now joins {" "} children into plain same-line spaces instead of
emitting them, because the lint rule flags {" "}.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ApTxEGsCt52QvsiF39nPiR
Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
…ec 019)

UI (T044, T045, T073, T095, per the 83c958d design):
- LanguagePicker in ThemeSettings; CompactLanguagePicker on Login and
  Share (browser-local gameplane-language.choice).
- Admin settings: Language card under General (config section i18n)
  and a per-sink language field in Notifications (Instance default).
- AdminSettings.test.tsx and AdminSettings_sections.test.tsx: 13 Save
  button queries scoped to the General region, approved by valgul
  because the Language card adds a second "Save changes" button.

String migration (T053-T056 waves, not ticked yet): components, routes
and lib move to useOptionalIntl().formatMessage with web.* ids;
i18n/locales/en/app.json regenerated (2611 messages);
src/components/settings/** added to MIGRATED_I18N_PATHS.

Local checks: tsc --noEmit clean, i18n-migrate --check src 0 findings.
Not done: T057, T059 (used ids must include API and operator ids),
T060, T089 descriptions pass, T100 en-XA run, raw err.message sites,
and recording the event-id and Local-accounts decisions in
OPEN-DECISIONS.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ApTxEGsCt52QvsiF39nPiR
Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
Brings in master up to 4b11c41. Makefile conflicted on the lint
target: master added check-features-sync and this branch added
check-i18n; the target now runs both.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
endpoints.ts now builds its legacy clients from @/lib/api at import time
(master). I18nProvider imported it statically, so every module that only
needs useLanguage pulled that in, and the Share page suites, which mock
@/lib/api without api, failed to load.

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

The string migration swapped the row's raw kind ("webhook") for the
translated picker label ("Webhook"), which the live admin-notifications
e2e spec and master's behaviour do not expect. The kind picker keeps the
translated labels.

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

LanguagePicker: the completeness label sits in the radio's label, so
assert the radio's accessible name instead of searching inside the
input element (drops the now unused within import).

Share language: wait for the resolved card before opening the menu, so
the loading card's picker is not the one clicked.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claude <noreply@anthropic.com>
Translator descriptions replace the codemod stubs for every message in
web/src/components/**, so Weblate shows what each text is and where it
appears. The empty select options and the password removal note in
ConfigFields render through intl (the exported English constants stay
for the tests). src/components/** joins MIGRATED_I18N_PATHS and
i18n/locales/en/app.json is regenerated. The English is unchanged.

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

Translator descriptions replace the codemod stubs in Login, Share,
NotFound, Dashboard, Servers, Clusters and Cluster; the routes join
MIGRATED_I18N_PATHS and i18n/locales/en/app.json is regenerated. The
English is unchanged.

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

Translator descriptions replace the codemod stubs in CreateServer,
ServerDetail and every server tab. The Backup now hints, the console
connect and disconnect lines and the mod provider subtitle render
through intl. The files join MIGRATED_I18N_PATHS and
i18n/locales/en/app.json is regenerated. The English is unchanged.

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

Translator descriptions replace the codemod stubs in Modules, Backups,
Users, AuditLog, AdminLogs, AdminSettings and ThemeSettings. Audit
labels move to the audit.* namespace from data-model.md (audit.verb,
audit.noun, audit.action, audit.access), still composed from method and
path, with unknown routes shown as before (FR-029). The install-ID
reset error renders through intl. The routes join MIGRATED_I18N_PATHS
and i18n/locales/en/app.json is regenerated. The English is unchanged.

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

conditionMessageText looks up condition.<type>.<reason> (lower-cased)
in the active catalog and formats it with status.conditionArgs[type].
English locales, missing entries and entries whose arguments the
operator did not record keep the operator's English message, so the
English does not change. ServerDetail (Progressing and Ready), the
Overview tab and the Networking address assignment use it; the
Networking parser still reads the English message.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
useModuleText and useModuleTexts read a module's translations from
GET /modules/{name}/locales/{tag} and format them, with the template
text as the fallback for every id the module does not translate (FR-032).
The create-server wizard (template cards and search, versions,
configuration fields, review and preview) and the Version and Game
configuration settings use them; ConfigFields takes an optional text
accessor. English makes no request and renders exactly as before.

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

MIGRATED_I18N_PATHS becomes src/**/*.{ts,tsx} (FR-022, SC-001); test
files and the src/test helpers stay exempt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
The i18n step builds the list of ids the code uses (extracted dashboard
ids, API registry ids and the operator condition ids from the new
condition-ids -ids flag) and passes it to i18n-check --used-ids
(FR-023). CI also runs the condition-ids drift guard now; make
check-i18n does the same locally. No unused entries were reported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
docs/translating.md gains a section for developers (intl pattern, ids,
descriptions, npm run i18n:extract, the codemod, lint and CI checks,
and the helpers for API, condition and module text); web/specs.md
records the lint scope, the unused-id check and the new helpers.

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

npm run screenshots:pseudo starts the dev server with VITE_I18N_PSEUDO=1
and runs the screenshot suite as the en-XA Playwright project. The specs
keep driving the UI in English; capture.ts switches the dashboard to
en-XA for each screenshot through window.gameplaneI18nTest (installed by
I18nProvider only when en-XA is listed, so never in a release), writes
the capture to test-results/en-XA and soft-fails on text that is cut
off without a title to reveal it (FR-010), then switches back. A new
"en-XA screenshots" job in visual-diff.yaml runs it and uploads the
captures. Decisions OD-018 and OD-019 (valgul, 2026-10-16).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
The first en-XA run found only data text that is already truncated in
English: server names, locations and addresses on the mobile Servers
cards, endpoint hosts on the Overview tab, module summaries and file
names. Each truncated element now carries a title with its full text
(FR-010, OD-020); nothing changes visually. The capture helper also
creates nested folders for ids such as legacy/dashboard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com>
@ValgulNecron
ValgulNecron marked this pull request as ready for review October 16, 2026 01:32

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