Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
d239616
feat(i18n): add shared i18n module with ICU formatter, pack validator…
claude Oct 11, 2026
28bbca5
feat(web): add dashboard i18n runtime behind an English-only catalog
claude Oct 11, 2026
87b9655
chore(web): keep the MSW worker script at master's version
claude Oct 11, 2026
59cc486
feat(api): add server message registry and opt-in problem+json errors
claude Oct 11, 2026
04a4f8b
ci(i18n): check language packs, drift and fixtures in the web job
claude Oct 11, 2026
a57baca
chore(web): restore tsconfig.tsbuildinfo to master's version
claude Oct 11, 2026
805828a
docs(specs): tick 019 foundation tasks T001-T034 and T048
claude Oct 11, 2026
4e9a33d
fix(i18n): write packs through os.Root and apply De Morgan simplifica…
claude Oct 11, 2026
372eb9c
fix(web): give the server-message lookup a translator description
claude Oct 11, 2026
6b655ad
fix(i18n): fall back to the other branch for an unmatched select value
claude Oct 12, 2026
4ecccd3
feat(api): save each user's language and add the instance default lan…
claude Oct 11, 2026
cc34e8c
feat(web): feed the user's and instance's language into the i18n prov…
claude Oct 11, 2026
ef03b1f
docs(specs): tick 019 US1 backend and US7 config tasks
claude Oct 11, 2026
da2cc4e
feat(operator): publish condition args and condition.* message ids
claude Oct 12, 2026
acf94b6
feat(api): render notifications in each destination's language
claude Oct 12, 2026
8416427
feat(api): localizable 4xx errors through the message registry
claude Oct 12, 2026
0e03ed1
feat(i18n): drop invalid pack entries from the dashboard build
claude Oct 12, 2026
7d7e26a
ci: label translation-only PRs as docs
claude Oct 12, 2026
8c53dc5
docs: add the translator guide and tick 019 US2 tasks
claude Oct 12, 2026
38ade2f
test(web): expect the problem+json Accept header on API reads
claude Oct 12, 2026
70a9bed
feat(gp-module): derive, validate, stamp and package module translations
claude Oct 12, 2026
8a29c23
feat(operator): publish module translations to a locales ConfigMap
claude Oct 12, 2026
661167e
feat(api): serve module translations and tick 019 US6 backend tasks
claude Oct 12, 2026
a01d6fb
fix(operator): cap total module translation reads at 4 MiB
claude Oct 12, 2026
94a0101
feat(web): pseudo-language en-XA behind VITE_I18N_PSEUDO
claude Oct 12, 2026
f52da42
docs: describe localization in architecture docs and the changelog
claude Oct 12, 2026
37519b6
feat(gp-module): report YAML names that cannot become translation ids
claude Oct 12, 2026
ad4e684
feat(design): 019 language pickers and settings fields
ValgulNecron Oct 12, 2026
c0f0aba
chore: merge master into claude/project-thread-awjqv0
ValgulNecron Oct 12, 2026
585b752
test(web): mock GET /auth/locale in browser mock mode (approved by va…
claude Oct 12, 2026
aa73103
test(i18n): TestModulePacks fixture template yields the id it tests (…
claude Oct 12, 2026
3064551
fix(web): build i18n-check before running it in the image's i18n stage
claude Oct 12, 2026
f80f937
Merge remote-tracking branch 'origin/master' into claude/project-thre…
claude Oct 12, 2026
45c29e4
chore(i18n): go mod tidy
claude Oct 12, 2026
ad20922
fix(gp-module): check i18n command writes and stamp through os.Root
claude Oct 12, 2026
b1b2508
fix(operator): write condition ids through os.Root in the generator
claude Oct 12, 2026
4d69d0b
chore: merge master into claude/project-thread-awjqv0
claude Oct 12, 2026
45f060d
test: fix spec 019 test fixtures and lint findings
claude Oct 12, 2026
2ec9b39
test(i18n): use Template/Modèle in the module id check fixtures
claude Oct 12, 2026
b9540c2
test(operator): clean up servers in the AddressInUse args envtest
claude Oct 14, 2026
53794a3
chore: merge master into claude/project-thread-awjqv0
claude Oct 14, 2026
5adac93
chore: merge master into claude/project-thread-awjqv0
claude Oct 14, 2026
3b84f01
chore: merge master into claude/project-thread-awjqv0
claude Oct 14, 2026
0107fde
fix(api): renumber the user language migration to 018
claude Oct 14, 2026
4e35ff8
chore: merge master into claude/project-thread-awjqv0
claude Oct 15, 2026
0ed0b66
feat(web): i18n-migrate codemod for the string migration (spec 019 T052)
ValgulNecron Oct 15, 2026
83c958d
feat(design): 019 language pickers and settings fields
ValgulNecron Oct 15, 2026
023de5c
feat(web): i18n-migrate --check lint proxy and space joining (spec 019)
ValgulNecron Oct 15, 2026
82ddaef
feat(web): language pickers, settings fields and string migration (sp…
ValgulNecron Oct 15, 2026
1d744c3
chore: merge master into claude/project-thread-awjqv0
claude Oct 15, 2026
d74a43d
fix(web): load the locale endpoint lazily in AppI18nProvider (spec 019)
claude Oct 15, 2026
0643b74
fix(web): show the stored sink kind in the notification sink row (spe…
claude Oct 15, 2026
59a3d66
test(web): fix two spec 019 language picker assertions (signed off by…
claude Oct 15, 2026
346352a
feat(web): finish the components string migration (spec 019 T053)
ValgulNecron Oct 15, 2026
f0c1a46
feat(web): finish the sign-in, share and fleet routes migration (spec…
ValgulNecron Oct 15, 2026
aa00e7b
feat(web): finish the create-server and server tabs migration (spec 0…
ValgulNecron Oct 15, 2026
7476814
feat(web): finish the admin, audit and module routes migration (spec …
ValgulNecron Oct 15, 2026
79510b0
feat(web): show operator condition messages in the user's language (s…
ValgulNecron Oct 16, 2026
5692483
feat(web): show module text in the user's language (spec 019 T089)
ValgulNecron Oct 16, 2026
f514e97
feat(web): apply the formatjs lint rules to all dashboard source (spe…
ValgulNecron Oct 16, 2026
2c0005e
ci: report unused English reference entries (spec 019 T059)
ValgulNecron Oct 16, 2026
bbf24b6
docs: document the developer workflow for dashboard text (spec 019 T060)
ValgulNecron Oct 16, 2026
656ce95
test(web): add the en-XA screenshot run with a clipping check (spec 0…
ValgulNecron Oct 16, 2026
9226085
fix(web): reveal cut-off data text on hover (spec 019 T100)
ValgulNecron Oct 16, 2026
a009569
docs(019): tick T100 after a green en-XA run
ValgulNecron Oct 16, 2026
5c1f0a2
fix(api): let every user save their own language (spec 019)
claude Oct 16, 2026
542c266
fix(web): keep the image build going when a language pack has finding…
claude Oct 16, 2026
fdfe953
ci: run the visual workflow when the English catalog changes (spec 019)
claude Oct 16, 2026
b7d55a2
fix(web): make the remaining dashboard text translatable (spec 019)
claude Oct 16, 2026
23ad4e1
Merge master into claude/project-thread-awjqv0 (spec 019)
claude Oct 16, 2026
5858d1c
fix(web): use the allowed <b> tag in the merged sentences (spec 019)
claude Oct 16, 2026
a62e742
Merge master into claude/project-thread-awjqv0 (spec 019)
claude Oct 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .devcontainer/post-create.sh
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ log "fetching envtest binaries for K8s 1.36.2"
# ---------- Go module cache warmup ----------
# Go modules from go.work. Derived from 'use' entries in go.work.
# Keep in sync with go.work when adding/removing modules.
for m in agent api audit-syslog-bridge capture-sidecar gameaction gameproto mcp-server netguard operator sentinel svcutil telemetry-receiver telemetryschema test/e2e tunnel; do
for m in agent api audit-syslog-bridge capture-sidecar gameaction gameproto i18n mcp-server netguard operator sentinel svcutil telemetry-receiver telemetryschema test/e2e tunnel; do
log "go mod download ($m)"
( cd "$m" && go mod download )
done
Expand Down
14 changes: 14 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,20 @@ updates:
telemetryschema-minor-patch:
update-types: ["minor", "patch"]

- package-ecosystem: "gomod"
directory: "/i18n"
schedule:
interval: "weekly"
day: "monday"
time: "03:00"
commit-message:
prefix: "chore(deps)"
include: "scope"
open-pull-requests-limit: 5
groups:
i18n-minor-patch:
update-types: ["minor", "patch"]

- package-ecosystem: "gomod"
directory: "/telemetry-receiver"
schedule:
Expand Down
68 changes: 66 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ jobs:
- 'audit-syslog-bridge/**'
- 'telemetry-receiver/**'
- 'telemetryschema/**'
- 'i18n/**'
- 'sentinel/**'
- 'mcp-server/**'
- 'capture-sidecar/**'
Expand All @@ -143,7 +144,9 @@ jobs:
- 'hack/check-govulncheck_test.sh'
- 'hack/testdata/govulncheck/**'
web:
# Language packs feed the dashboard build and the i18n checks in the web job.
- 'web/**'
- 'i18n/**'
charts:
- 'charts/**'
# The chart embeds a copy of the root signing key; the drift
Expand Down Expand Up @@ -698,7 +701,7 @@ jobs:
strategy:
fail-fast: false
matrix:
module: [netguard, gameaction, gameproto, telemetryschema, operator, agent, api, sentinel, audit-syslog-bridge, telemetry-receiver, mcp-server, capture-sidecar, svcutil, tunnel, gp-module, test/e2e]
module: [netguard, gameaction, gameproto, telemetryschema, i18n, operator, agent, api, sentinel, audit-syslog-bridge, telemetry-receiver, mcp-server, capture-sidecar, svcutil, tunnel, gp-module, test/e2e]
exclude: ${{ fromJSON(needs.changes.outputs.lint-exclude) }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand Down Expand Up @@ -777,7 +780,7 @@ jobs:
strategy:
fail-fast: false
matrix:
module: [netguard, gameaction, gameproto, telemetryschema, operator, api, agent, audit-syslog-bridge, telemetry-receiver, sentinel, mcp-server, capture-sidecar, svcutil, tunnel, gp-module]
module: [netguard, gameaction, gameproto, telemetryschema, i18n, operator, api, agent, audit-syslog-bridge, telemetry-receiver, sentinel, mcp-server, capture-sidecar, svcutil, tunnel, gp-module]
exclude: ${{ fromJSON(needs.changes.outputs.go-exclude) }}
arch: [amd64, arm64]
steps:
Expand Down Expand Up @@ -890,6 +893,67 @@ jobs:
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with: { node-version: "24", cache: "npm", cache-dependency-path: web/package-lock.json }
- run: npm ci
- uses: ./.github/actions/go-cache
with:
key-suffix: web-i18n
- name: i18n language manifest
# The listing rule (80% complete, ltr, not en-XA) lives in the shared
# Go checker, so the manifest the dashboard build reads comes from it.
run: npm run i18n:manifest
- name: i18n checks
working-directory: .
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
run: |
set -euo pipefail
(cd web && npm run i18n:extract-only)
(cd api && go run ./cmd/messages-export > "$RUNNER_TEMP/registry.json")
(cd operator && go run ./hack/condition-ids -check -app ../i18n/locales/en/app.json -ids "$RUNNER_TEMP/condition-ids.txt")
{
jq -r 'keys[]' web/src/generated/i18n/extracted.json
jq -r '.[].id' "$RUNNER_TEMP/registry.json"
cat "$RUNNER_TEMP/condition-ids.txt"
} | sort -u > "$RUNNER_TEMP/used-ids.txt"
base_flag=()
if [ -n "${BASE_SHA:-}" ] && [ "$BASE_SHA" != "0000000000000000000000000000000000000000" ]; then
if git fetch --no-tags --depth=1 origin "$BASE_SHA" 2>/dev/null \
&& git cat-file -e "$BASE_SHA:i18n/locales" 2>/dev/null; then
mkdir -p "$RUNNER_TEMP/base"
git archive "$BASE_SHA" i18n/locales | tar -x -C "$RUNNER_TEMP/base"
base_flag=(--base "$RUNNER_TEMP/base/i18n/locales")
fi
fi
cd i18n
go run ./cmd/i18n-check \
--extracted ../web/src/generated/i18n/extracted.json \
--registry "$RUNNER_TEMP/registry.json" --check-server \
--used-ids "$RUNNER_TEMP/used-ids.txt" \
"${base_flag[@]}" \
--summary "$GITHUB_STEP_SUMMARY" \
locales
- name: i18n-check self-test on seeded defective packs
# SC-007: every fixture must make the checker fail with exactly its
# own finding code (needs-review is a notice, so it passes).
working-directory: i18n
run: |
set -euo pipefail
go build -o "$RUNNER_TEMP/i18n-check" ./cmd/i18n-check
for dir in testdata/defective/*/; do
code=$(basename "$dir")
args=()
if [ -f "$dir/args" ]; then
read -r -a args < <(sed "s#{dir}#${dir%/}#g" "$dir/args")
fi
set +e
out=$("$RUNNER_TEMP/i18n-check" "${args[@]}" "${dir%/}")
status=$?
set -e
want=1; [ "$code" = needs-review ] && want=0
if [ "$status" -ne "$want" ] || ! grep -q "\[$code\]" <<<"$out"; then
echo "::error::fixture $code: exit $status (want $want), output:"; echo "$out"; exit 1
fi
echo "ok $code"
done
- name: eslint
run: npm run lint
- run: npm run build
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/publish-edge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ on:
- "gameaction/**"
- "gameproto/**"
- "telemetryschema/**"
- "i18n/**"
- "svcutil/**"
- "gp-module/**"
- "audit-syslog-bridge/**"
Expand Down
50 changes: 50 additions & 0 deletions .github/workflows/visual-diff.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ on:
branches: [master]
paths:
- 'web/**'
- 'i18n/locales/**'
- 'assets/design-export/**'
- '.github/workflows/visual-diff.yaml'
pull_request:
paths:
- 'web/**'
- 'i18n/locales/**'
- 'assets/design-export/**'
- '.github/workflows/visual-diff.yaml'
workflow_dispatch:
Expand Down Expand Up @@ -80,3 +82,51 @@ jobs:
path: web/e2e/screenshots/*.png
retention-days: 14
if-no-files-found: warn

pseudo-locale:
name: en-XA screenshots (spec 019)
runs-on: ubuntu-latest
permissions:
contents: read
timeout-minutes: 30
defaults:
run:
working-directory: web
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
node-version: "24"
cache: "npm"
cache-dependency-path: web/package-lock.json

- name: install dependencies
run: npm ci

- name: playwright browser cache
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: ~/.cache/ms-playwright
key: playwright-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('web/package-lock.json') }}
restore-keys: |
playwright-${{ runner.os }}-${{ runner.arch }}-

- name: install chromium
run: npx playwright install --with-deps chromium

- name: capture en-XA screenshots and check clipping
run: npm run screenshots:pseudo

- name: upload en-XA captures and clipping report
if: always()
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: en-XA-screenshots
path: |
web/test-results/en-XA
web/playwright-report
retention-days: 14
if-no-files-found: warn
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ web/coverage/
web/playwright-report/
web/test-results/
web/website-screenshots/
# Generated i18n artifacts (compiled catalogs, extracted messages, language manifest)
web/src/generated/
# Registry snapshot written by `make i18n-sync` and consumed by i18n-check
i18n/.registry.json
# Condition ids and used-id list written by `make check-i18n` (spec 019)
i18n/.condition-ids.txt
i18n/.used-ids.txt
# Filtered language packs written by i18n-check --valid-out for the dashboard build
i18n/.valid/
web/e2e/.portforward.pid
*.log
.npmrc.local
Expand Down
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,36 @@ reaches `1.0.0`. Pre-1.0 minor versions may contain breaking changes.
- **Nuclear Option player lists are structured:** the agent's player output
for JSON player lists now includes an entry per player (`steamId` and
`faction`), which the API uses for display-name lookup.
- **Language selection and translations (spec 019):** the dashboard can be
shown in another language. A user's language choice is saved to the account (or
to the browser when signed out), and an admin sets the instance default. Languages are
shared packs under `i18n/locales/`; a language is listed once its pack is at
least 80% translated, and anything missing falls back to English. French is
the first pack and is listed once it reaches 80%.
Dates, numbers and relative times follow the active language. A pseudo
language (`en-XA`) exists for maintainers behind `VITE_I18N_PSEUDO=1` and is
never listed in a release. See [`docs/translating.md`](docs/translating.md).
- **Translated server messages (opt-in `problem+json`):** API errors are sent
as `application/problem+json` with a stable `messageId` and arguments only
when the request lists that type in `Accept`; every other client keeps the
unchanged English plain-text body. The dashboard opts in and shows the
message in the active language. Game server condition messages carry
`status.conditionArgs` so the dashboard can translate them too; the English
`message` is unchanged.
- **Notification language per destination:** each notification destination
has an optional `language`; notifications and test sends to Discord, email
and webhooks are rendered in it, and destinations with none use the
instance default. Webhook payloads gain additive `language`, `title` and
`text` fields; existing fields are unchanged.
- **Module translations:** a module can ship `locales/<tag>.json` next to
`module.yaml` (OCI layer type `application/vnd.gameplane.module.locale.v1+json`
and the same files in every other source). The operator validates them on
install, stores the valid entries in a `<template>-locales` ConfigMap and
reports problems as the `LocalesValid` condition; install never fails
because of translations. Each file is read up to 900 KiB and all of a
bundle's files together up to 4 MiB. `gp-module i18n extract|stamp` and
`gp-module validate` support authoring. See
[`docs/module-authoring.md`](docs/module-authoring.md).

### Changed

Expand Down
6 changes: 4 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ netguard/ SSRF dial-guard: IsAllowed (operator module sources, API si
gameaction/ console-injection guard + command renderer (api, agent)
gameproto/ Minecraft/Terraria handshake parser (sentinel)
telemetryschema/ shared telemetry report contract (api, telemetry-receiver)
i18n/ shared language packs, ICU formatter, pack validator, i18n-check (api, web, operator, gp-module)
gp-module/ module authoring CLI: init/validate/preview/package
operator/ controller-runtime operator
api/v1alpha1/ CRD types (edit here, then `make generate manifests`); zz_generated.deepcopy.go GENERATED
Expand All @@ -60,7 +61,7 @@ test/e2e/ Kind E2E suite (//go:build e2e)
docs/ architecture, security, modules
design.pen canonical Pencil dashboard design
signing/ cosign public keys, dated legacy key + cross-signature (image + module)
go.work links all 16 Go modules (incl. test/e2e)
go.work links all 17 Go modules (incl. test/e2e)
Makefile canonical task runner
```

Expand All @@ -75,7 +76,7 @@ After cloning: `git submodule update --init` (`modules/` required for `make dev-
- **E2E conventions:** register new tests in `test/e2e/buckets.sh`; `t.Parallel()` + unique names; guard shared resources (`ociPushMu` for module pushes, `ensureResticRepo(t)` for shared backup repos). Rate limits per cluster: IP burst 10 (5/min), user burst 6 (3/min) — cap an API bucket at ~7 admin logins.
- **Lint:** `make lint` = gofmt, go vet, golangci-lint, ESLint, `check-specs`, `check-doc-versions`, `check-links`.

**Coverage minimums:** netguard 91, gameaction 91, gameproto 90, telemetryschema 90, gp-module 80, operator 72, api 80, agent 90, svcutil 90; audit-syslog-bridge, telemetry-receiver, sentinel, capture-sidecar, mcp-server, tunnel 70; web 92 lines / 76 functions / 82 branches / 92 statements.
**Coverage minimums:** netguard 91, gameaction 91, gameproto 90, telemetryschema 90, i18n 90, gp-module 80, operator 72, api 80, agent 90, svcutil 90; audit-syslog-bridge, telemetry-receiver, sentinel, capture-sidecar, mcp-server, tunnel 70; web 92 lines / 76 functions / 82 branches / 92 statements.

## Core rules

Expand Down Expand Up @@ -116,6 +117,7 @@ After cloning: `git submodule update --init` (`modules/` required for `make dev-
| gameaction | Go | validates console input against schemas; escapes injection |
| gameproto | Go | Minecraft/Terraria wire parser for connection filtering |
| telemetryschema | Go | stdlib-only shared telemetry report contract: types, strict decode, official module catalog, report signing (api, telemetry-receiver) |
| i18n | Go | shared language packs (`go:embed`), ICU MessageFormat subset, pack validator, `i18n-check` CI tool (api, web, operator, gp-module) |
| gp-module | Go | module CLI: scaffold, offline validate, dry-run preview, OCI package |
| svcutil | Go | stdlib-only env + graceful shutdown (`RunHTTP`) |
| operator | Go, controller-runtime | authoritative reconciler for 9 CRDs (`GameServer`, `GameTemplate`, …) |
Expand Down
19 changes: 17 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ KUBECONFIG_ENV :=
KUBE_CONTEXT := kind-$(KIND_CLUSTER)
endif

GO_MODULES := netguard gameaction gameproto telemetryschema gp-module operator api agent audit-syslog-bridge telemetry-receiver sentinel mcp-server capture-sidecar svcutil tunnel
GO_MODULES := netguard gameaction gameproto telemetryschema i18n gp-module operator api agent audit-syslog-bridge telemetry-receiver sentinel mcp-server capture-sidecar svcutil tunnel
GO_INTEGRATION_MODULES := operator api

# test/e2e is a separate module (its own go.mod, excluded from the coverage
Expand Down Expand Up @@ -267,6 +267,21 @@ test-features-sync: ## Run the fixture tests for hack/check-features-sync.sh
check-telemetry-catalog: ## Verify telemetryschema/catalog.txt matches the chart's official module list
hack/check-telemetry-catalog.sh

.PHONY: i18n-sync
i18n-sync: ## Write the API message registry (server.* and notify.*) and the operator condition.* ids into the English reference (i18n/locales/en/app.json)
cd api && go run ./cmd/messages-export > ../i18n/.registry.json
cd i18n && go run ./cmd/i18n-check --registry .registry.json --sync-server locales
rm -f i18n/.registry.json
cd operator && go run ./hack/condition-ids -app ../i18n/locales/en/app.json

.PHONY: check-i18n
check-i18n: ## Verify language packs, server.*, notify.*, operator condition.* drift, dashboard extraction drift and unused reference ids (spec 019)
cd web && npm run i18n:extract-only
cd operator && go run ./hack/condition-ids -check -app ../i18n/locales/en/app.json -ids ../i18n/.condition-ids.txt
cd api && go run ./cmd/messages-export > ../i18n/.registry.json
cd i18n && { jq -r 'keys[]' ../web/src/generated/i18n/extracted.json; jq -r '.[].id' .registry.json; cat .condition-ids.txt; } | sort -u > .used-ids.txt
cd i18n && status=0; go run ./cmd/i18n-check --extracted ../web/src/generated/i18n/extracted.json --registry .registry.json --check-server --used-ids .used-ids.txt locales || status=$$?; rm -f .registry.json .condition-ids.txt .used-ids.txt; exit $$status

.PHONY: check-dev-load-images
check-dev-load-images: ## Verify `dev-load` loads every image `images` builds
hack/check-dev-load-images.sh
Expand All @@ -281,7 +296,7 @@ test-up-registry: ## Run the fixture tests for deploy/kind/up.sh's registry/cont
hack/test-up-registry.sh

.PHONY: lint
lint: check-doc-versions check-links check-features-sync check-specs check-telemetry-catalog check-dev-load-images check-coverage-gaps test-up-registry lint-go lint-web ## Run all linters
lint: check-doc-versions check-links check-features-sync check-specs check-telemetry-catalog check-i18n check-dev-load-images check-coverage-gaps test-up-registry lint-go lint-web ## Run all linters

.PHONY: lint-go
lint-go: ## Run golangci-lint across all modules
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,9 @@ Design changes go through `design.pen` (Pencil) before any UI code is
written. See [`docs/contributing.md`](docs/contributing.md) for the
full guide: code style, test tiers, and the PR process.

Translations are welcome. See [`docs/translating.md`](docs/translating.md)
to add or improve a language pack.

## License

[AGPL-3.0-or-later](./LICENSE). Any network-accessible deployment of a
Expand Down
5 changes: 3 additions & 2 deletions api/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
FROM --platform=$BUILDPLATFORM golang:1.27-alpine AS build
WORKDIR /src

# netguard, gameaction, gp-module, and telemetryschema are in-repo modules the API depends
# on via local replaces (../netguard, ../gameaction, ../gp-module, ../telemetryschema);
# netguard, gameaction, gp-module, telemetryschema, and i18n are in-repo modules the API depends
# on via local replaces (../netguard, ../gameaction, ../gp-module, ../telemetryschema, ../i18n);
# copy them first so `go mod download` and the build resolve them.
COPY netguard/ ./netguard/
COPY gameaction/ ./gameaction/
COPY gp-module/ ./gp-module/
COPY telemetryschema/ ./telemetryschema/
COPY i18n/ ./i18n/
COPY api/go.mod api/go.sum* ./api/
WORKDIR /src/api
RUN go mod download
Expand Down
4 changes: 4 additions & 0 deletions api/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,10 @@ func main() {
// registry per request so config saves apply without a restart.
// No version/host/count — login-privacy.
r.Get("/providers", handlers.AuthProvidersHandler(authRegistry))
// Pre-auth: the instance default language for the login page. Only the
// tag is returned, never more (login privacy); not rate-limited, like
// /providers.
r.Get("/locale", handlers.AuthLocaleHandler(store))
// Rate-limit /login specifically — argon2id is ~200 ms of
// single-core CPU per attempt, so an unlimited path invites a
// trivial DoS.
Expand Down
19 changes: 19 additions & 0 deletions api/cmd/messages-export/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Command messages-export writes the API message registry as JSON to stdout,
// together with the notify.* notification text. The i18n-sync Makefile target
// feeds the output to i18n-check.
package main

import (
"fmt"
"os"

"github.com/GameplanePanel/gameplane/api/internal/messages"
"github.com/GameplanePanel/gameplane/api/internal/notify"
)

func main() {
if err := messages.ExportJSON(os.Stdout, notify.Messages()...); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
Loading
Loading