Skip to content

PRD P5-W1: the i18n infrastructure (namespaces, extraction, pseudo-locale check) - #47

Merged
compgeniuses merged 3 commits into
consolidationfrom
prd/P5-A01
Oct 24, 2026
Merged

compgeniuses merged 3 commits into
consolidationfrom
prd/P5-A01

Conversation

@compgeniuses

Copy link
Copy Markdown
Contributor

P5-W1: the i18n infrastructure. One of the wave's two tasks; see "Not in this PR" below.

P5-A01 — namespaces, extraction, and the pseudo-locale check

  • Namespaces — the app's strings organised so a translator works on one module without loading the others, with the existing flat files migrated rather than duplicated.
  • Extraction — a way to find strings that are in the code but not in the catalogue, and entries in the catalogue nothing uses, run, with what it found reported.
  • Translation-platform sync — the file shape a platform imports and exports, with the round trip proved by a test rather than described.
  • A pseudo-locale CI check — a locale that makes untranslated and over-long strings visible, wired into the repo's own CI, because a check nobody runs is not a check.

Verified

node scripts/prd/gate.mjs task --base schoolpilot/consolidation → GATE PASSED (7 checks, 0 failed): G1 typecheck 2305/2500 (no new errors), G2 contract scan, G3 21 test files / 105 tests passed, G4 7 test files changed, G5–G7 green.

Not in this PR

P5-A02 (locale formats and RTL layout) is not here: its implementer session failed on the model provider's quota before it wrote anything, and its worktree is empty. The wave is opened with A01 so the work that exists reaches consolidation; A02 follows as its own change.

This is the phase document's own allowance, not a shortcut: P5 "Depends on: P2 engine for curriculum packs; P5-W1 (i18n infrastructure) may start during P1", and P2 is merged. P6 stays queued — it depends on P2, P3 and P4.

The implementer session failed with "Free usage limit reached" part-way through this task. This
commit preserves what was on disk in its worktree so nothing is lost while the work is finished by
hand. It is NOT a completed task: the acceptance has not been met, the gate has not been run, and no
PR should be opened from this branch until it is.
Namespaces: the 65 per-module i18n files and the core module's per-namespace
files are folded into src/i18n/locales/<locale>/<namespace>.json and removed, so
one namespace is one file a translator can open without loading another module.
The consolidation is lossless: the catalogue read back from disk matches the
catalogue the old loader built (checksum 8b10f3af). The cross-namespace
navigation fan-in is gone, which is what made an export/import round trip lossy.

Extraction: scripts/i18n/extract.ts reports keys the code asks for that the
catalogue cannot answer, keys nothing uses, and page descriptions whose whole
value repeats the field name. --fix moves inline English defaults into the
catalogue; --check fails on any untranslated core string. It found 442 keys the
code asks for and en lacks (435 with an English default in the code), 2212
catalogue keys nothing uses, 16 placeholder page descriptions and 29 dynamic key
expressions it will not guess at.

Platform sync: src/i18n/platform.ts defines the file shape (one plain namespace
file per locale plus a manifest with a key count and checksum) and
scripts/i18n/sync.ts round-trips it; `roundtrip --target fr|sw|rw` prints that
every namespace came back identical.

Pseudo-locale: src/i18n/pseudo.ts accents, marks and widens every English string,
preserving interpolations and tags. The loader installs it as `pseudo` and the
language switcher offers it, so untranslated text stays plain ASCII and over-long
labels show their extra width. scripts/i18n/check-pseudo.ts fails on any string
left untransformed; CI runs all three checks in the gate job.

The pre-commit translation-completeness hook needed key parity, so fr/sw/rw
gained the 2325 keys only en had, filled with the English source; 53 English
labels were written for inventory items/suppliers and library books/fines, where
only French had the object shape.

Requirements: FR-24
Gate: task PASS - tsc 2305/2500, missingNames 0/0, contracts 0/0, related tests 16 files / 89 tests passed, eslint on changed files PASS
The repo-wide mojibake check (scripts/prd/fix-encoding.mjs --check) reads the accented pseudo-locale output as double-encoded UTF-8, so fix-encoding.test.mjs's '--check passes on src/' failed on CI - the only new failing file in the whole suite, and the reason PR #47 was red.

Building the expectation from escapes is what this repo already does for exactly this reason: fix-encoding.test.mjs itself builds its mojibake samples at runtime so the file stays clean UTF-8.
@compgeniuses
compgeniuses merged commit 80092e7 into consolidation Oct 24, 2026
4 checks passed
compgeniuses added a commit that referenced this pull request Oct 27, 2026
… format, KE payroll pack, NG and UG packs (#53)

* chore(prd): record P5-W1 as done and open P5-W2 in the ledger, progress, wave map and handoff [P5-W2]

The ledger still showed P5-A01 and P5-A02 as todo although #47 and #50 merged them. They are done, with their
commits. P5-W2's four tasks are in progress on their own branches and worktrees. The wave map had gone stale
(it listed P4-W3 as in flight and P3 as owed a gate re-run); it now lists #49 to #52 as merged, P3 and P4 as
complete, and what P5-W2 has in flight. The handoff opens with where the work stands and how to resume it.

* chore(prd): record P5-A03 verified, P5-B01, B02 and C01 in review [P5-W2]

* feat(calendar): the Ethiopian calendar behind a calendar abstraction, in the date fields [P5-A03]

A calendar system converts to and from a Gregorian local day, names its months in a locale and steps through them:
the Gregorian calendar, unchanged, and the Ethiopian one (twelve months of 30 days and Pagume of 5 or 6), converted
through the Julian day number. Every day from 1900 to 2100 round-trips, every Ethiopian date of 1893 to 2092
round-trips, and every day from 1990 to 2060 matches the platform's ICU Ethiopic calendar.

DatePicker and DateField take a calendar, defaulting to the locale's own (Ethiopian for an ET locale), and show, type
and step through it while the value stays a Gregorian day. PageUp and PageDown on the Gregorian grid now stop at
the end of a shorter month instead of overflowing into the month after.

Requirements: FR-24
Gate: task PASS - tsc 2306/2500, missingNames 0/0, contracts 0, related tests 488 passed (34 files), eslint clean, secrets and dependencies clean

* feat(i18n): Kiswahili for the core flows and the Kenyan packs, with a completeness check [P5-B01]

Adds src/i18n/completeness.ts (missing, extra, empty, untranslated and placeholder checks against English), the
check-locales script wired into i18n:check, and a vitest suite over the real catalogue. Translates the common and
navigation namespaces to sw (draft, no native review), drops 21 stale navigation keys English no longer has, completes
the ke-844 sw pack locale, and records the term choices in docs/i18n/glossary.md.

Requirements: FR-24
Gate: task PASS - 8 checks, 0 failed; typecheck 2306/2500; related tests 24 passed

* feat(i18n): French for the core flows [P5-B02]

Adds fr to the locales the completeness check requires, translates the common and navigation namespaces to fr
(draft, no native review), repairs the mojibake and unaccented strings in fr navigation, drops 18 stale navigation
keys English no longer has, splits the English-identical allowlist into shared and per-locale lists so one locale
cannot hide an untranslated string behind another's cognates, and adds the French column to the glossary.

Requirements: FR-24
Gate: task PASS - 8 checks, 0 failed; typecheck 2306/2500; related tests 24 passed

* chore(prd): record P5-A03, B01 and B02 as done on the wave branch [P5-W2]

* feat(i18n): Portuguese and Arabic for the core flows [P5-B03]

Adds pt and ar to the supported locales and the locales the completeness check requires, registers both in the
language switcher (Português, the Arabic name in Arabic script; ar is right to left), and translates the common
and navigation namespaces to both (drafts, no native review). Allowlists the Portuguese "Portal", extends the
registry and RTL tests, and adds the Portuguese and Arabic columns to the glossary.

Requirements: FR-24
Gate: task PASS - 8 checks, 0 failed; typecheck 2306/2500; related tests 101 passed

* chore(prd): record P5-B03 done, P5-D01 in progress [P5-W2]

* feat(country): country pack format and the KE country pack [P5-C01]

Adds packs/country/schema.json (JSON Schema 2020-12) and the KE pack: identifier
schemes with validation, the 47 counties, public holidays with the Sunday rule and
statutory returns without rates, each datum marked verified or draft with its source.
src/domain/country loads the packs and serves schemes, validation, holidays and the
address hierarchy. The admission form's identifier lists and value validation, the KE
address schema and the county select of both application forms now come from the pack.

Requirements: FR-25
Gate: task PASS - typecheck 2306/2500, related tests 612 passed in 60 files, 8 test files changed, lint clean

* fix(packs): the KE pack cites Cap. 110 and the KRA housing-levy notice at addresses that resolve [P5-C01]

The independent verifier found both cited URLs dead (404 and a TLS failure); these are the working addresses of the same documents.

* chore(prd): record P5-C01 done; every P5-W2 task is integrated [P5-W2]

* docs(prd): the P5-W2 wave log [P5-W2]

* chore(prd): P5-D01 in review, P5-C02 and C03 in progress [P5-W3]

* docs(prd): handoff at the session-limit pause, with each stopped agent and how to resume it [P5-W3]

* feat(payroll): add the payroll pack interface and the Kenya payroll pack [P5-D01]

Payroll is an interface: src/domain/payroll loads a country payroll pack and
computes payslips and statutory return rows from it over the Money type, with
every rate, band and limit read from effective-dated pack data (no numeric
statutory literal exists in the code, a test enforces it). packs/payroll holds
the JSON Schema and the Kenya pack: PAYE bands and reliefs, NSSF years 1-4, SHIF,
the Affordable Housing Levy and the NITA levy, each entry with its fetched
official source; figures no official source confirmed stay verification: draft
and are listed in packs/payroll/ke/README.md, which says a payroll accountant
must confirm the pack before anyone is paid from it.

Requirements: FR-25
Gate: task PASS - 8 checks, 0 failed; typecheck 2306/2500, 72 related tests

* fix(payroll): the December 2024 PAYE entry is draft: it rests on a reading of KRA's notice that no source states [P5-D01]

The independent verifier found the entry marked verified while its NSSF-shares-the-pension-cap reading is an assumption. It is now draft and listed in the README; no figure changed.

* chore(prd): record P5-D01 done, P5-C03 in review [P5-W3]

* test(forms): the minDate DatePicker test no longer depends on today's date

It built a past day from a button's day number in the current month and skipped the assertion when none matched; with minDate in the future the picker opens on minDate's month, so the test failed from 2026-10-27 on consolidation too (it blocked every gate that selects it as a related test). It now uses fixed days, reads each button's data-date, asserts the day before minDate is disabled and minDate's own day is not, and fails when the picker ignores minDate. [P5-W2]

* feat(packs): Nigeria curriculum and country packs [P5-C03]

Adds ng-ube (Primary 1 to JSS 3, revised 9-year BEC subject lists) and ng-sss
(SS 1 to SS 3): two continuous assessments plus an exam, the WAEC-style A1 to
F9 scale, credit counts, class position and a terminal report with the
affective and psychomotor domains. Adds the NG country pack (NIN, states,
Public Holidays Act days, PAYE, pension and NSITF dates, no rates). The NG
address entry now comes from the pack. Unconfirmed rules stay draft.

Requirements: P5-C03
Gate: task PASS - tsc 2306/2500, missingNames 0/0, contracts all 0/0, related tests 615 passed (57 files), 2 test files, eslint clean

* chore(prd): record P5-C03 done [P5-W3]

* chore(prd): P5-C04 and C05 in progress [P5-W3]

* chore(prd): P5-C02 in review, P5-C06 in progress [P5-W3]

* feat(packs): uganda curriculum and country packs [P5-C02]

Adds ug-ple (P1-P7, PLE grades D1-F9 and divisions I-IV/U), ug-lsc (S1-S4
competency-based, UCE) and ug-uace (S5-S6, principal points and subsidiaries),
plus the UG country pack (identifiers, holidays, PAYE/NSSF returns). The UG
address entry now comes from the country pack.
Everything not confirmed from a fetched UNEB/NCDC/URA source stays draft and is
listed in each README: UNEB's live site was unreachable, so division ranges,
percent boundaries, UACE points and the Public Holidays Act text are unconfirmed.

Requirements: P5-C02
Gate: task PASS - 8/8 checks, typecheck 2306/2500, related tests 597 passed (57 files)

* chore(prd): record P5-C02 done [P5-W3]

* chore(prd): P5-C04 in review [P5-W3]

* feat(packs): Tanzania and Rwanda curriculum and country packs [P5-C04]

Adds tz-national (Standard I to VII, Form I to IV: PSLE paper grades, CSEE
grades, points and divisions over the seven core subjects) and rw-cbc (P1 to
P6, S1 to S3: the NESA seven-point scale and the PLE and O-level aggregates),
with the TZ and RW country packs (NIDA numbers, regions and provinces, public
holidays, PAYE, SDL and RSSB dates, no rates). The TZ and RW address entries
now come from the packs. Unconfirmed rules stay draft and are listed.

Requirements: P5-C04
Gate: pending

* chore(prd): record P5-C04 done, P5-C05 in review [P5-W3]

* ci: run the browser golden paths on Ubuntu 24.04, where Playwright 1.55 has a browser build

ubuntu-latest now gives 26.04 on the runners and 'playwright install --with-deps chromium' fails there with 'Playwright does not support chromium on ubuntu26.04-x64', failing PR #53's e2e job before any test runs. The other jobs keep ubuntu-latest. Revert when Playwright supports the newer image.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant