ADR-0019 §9 sweep (2/2): help blocks + modals + system notes

Final pass of the i18n migration sweep. Every user-visible
string in `src/` now flows through the catalog via `t!()`.

## Categories migrated in this commit

- **help.cli_banner** — the entire `cli::HELP_TEXT` const,
  formerly a 40-line `&'static str`, is now a YAML block in
  the catalog. The const is replaced by a thin
  `cli::help_text() -> String` wrapper that performs the
  catalog lookup. `main.rs` calls `help_text()` for both
  `--help` output and the args-parse error path. The two
  integration tests that referenced `HELP_TEXT` directly are
  updated.
- **help.in_app_body** — the in-app `help` command's body is
  one YAML block; `note_help` becomes 5 lines that iterate
  the lines and emit each as its own output row (preserving
  the renderer's "one logical line = one display row"
  invariant for accurate scroll math).
- **modal.*** — load picker, rebuild confirm, and save-as
  path-entry strings: rebuild_cancelled, load_cancelled,
  generic_cancelled, load_picker_nothing,
  path_entry_empty_name, path_entry_empty_path.
- **dsl.failed** — the `"<verb> <subject>" failed: <rendered>`
  wrapper around the friendly-error layer's translated
  message.
- **dsl.running** — the `running: <input>` echo line shown
  above each command's response. (Note: the en-US prefix
  "running: " is hardcoded in the parse-error caret-padding
  calculation. Translators changing the prefix must keep the
  width consistent — documented inline.)
- **advanced_mode.not_implemented** — the placeholder echo
  shown when SQL hits the unimplemented advanced-mode path
  (Q1 territory).
- **fatal.persistence** — the FATAL banner for
  PersistenceFatal events (ADR-0015 §8).
- **project.{load_path_missing,saveas_target_exists,**
  **import_zip_missing}** — runtime-side project-switch
  validation errors that surface via ProjectSwitchFailed.

## Catalog start-up ordering

`main.rs` now calls `friendly::catalog()` at the very top
(before args parsing) so `help_text()` works in both the
success path and the args-error path. A corrupted build
artefact still fails loudly with a useful panic; the
practical risk is essentially zero since the catalog is
`include_str!`'d at compile time and validated by the unit
test before shipping.

## Remaining literals

The only `note_*` calls in `src/` that still pass plain
strings are inside `#[cfg(test)]` modules — synthetic test
fixtures, not user-visible. The codebase passes the "every
user-visible string flows through the catalog" bar.

## Tally

610 tests passing (no change in count — pure refactor).
Clippy clean with nursery lints.

## What this closes

ADR-0019 §9 (migration sweep) — done.

ADR-0019 itself is now fully implemented:
- §1-§5: catalog + translator + voice + verbosity ✓ (`eac7e5b`)
- §6: row pinpointing + schema enrichment ✓ (`431645a`)
- §9: migration sweep ✓ (this + `aff528a`)
- §10: anchor phrases preserved throughout ✓
- The five "Out of scope" items remain explicitly bounded
  to future ADRs (advanced-mode SQL, settings persistence,
  pluralisation, runtime locale, value formatting,
  constraint management).
This commit is contained in:
claude@clouddev1
2026-05-09 22:29:28 +00:00
parent aff528aa3f
commit 720511ef29
8 changed files with 224 additions and 129 deletions
+130
View File
@@ -152,6 +152,103 @@ error:
empty_update:
headline: "UPDATE requires at least one assignment."
# ---- Help text (CLI banner + in-app `help` command) ------------------
help:
# CLI usage banner. Printed by `--help` / `-h` and on
# argument-parse failure. Multi-line block; consumers
# iterate lines or print as-is.
cli_banner: |
rdbms-playground — a TUI playground for relational database concepts
Usage:
rdbms-playground [options] [<project-path>]
Arguments:
<project-path> Path to an existing project directory.
Without this, a fresh auto-named temp
project is created in the data dir.
Options:
-h, --help Print this help and exit.
--theme <light|dark> Override theme (default: auto-detect).
--data-dir <PATH> Use PATH as the data root instead of
the OS-standard location for this run.
--log-file <PATH> Write tracing output to PATH.
--resume Open the most-recently-used project
(path tracked under <data-root>/last_project).
Errors out if no previous project is
recorded. Mutually exclusive with
<project-path>.
App-level commands (typed inside the app, available in both modes):
quit / q Exit cleanly.
mode simple|advanced Switch input mode.
help Show this list of commands in-app.
save Save the current temp project under a
chosen name (or `save as` to copy a
named project to a new location).
save as Always prompt for a target name/path.
new Close current, create a fresh temp.
load Open the project picker.
rebuild Rebuild playground.db from project.yaml
+ data/, with confirmation.
export [<path>] Write a zip of project.yaml + data/ to
<path> (relative paths under the data
root). Excludes playground.db and
history.log.
import <zip> [as <t>] Unpack <zip> into a new project and
switch to it. <t> overrides the target
name (else taken from the zip).
# In-app `help` command output. Same shape as
# `cli_banner` — multi-line block, consumers iterate
# lines and emit each as its own output row so scroll
# math stays accurate.
in_app_body: |
Supported commands:
quit / q — exit
help — this list
mode simple|advanced — switch input mode
messages — show current verbosity
messages short|verbose— switch error wording (verbose is the default)
rebuild — rebuild .db from project.yaml + data/ (with confirmation)
save — save current temp project under a name
save as — copy current project to a new name/path
new — close current, start a fresh temp project
load — open the project picker
export [<path>] — write a zip of project.yaml + data/ (excludes .db, history.log)
import <zip> [as <t>] — unpack a zip and switch to the new project
DSL data commands (in simple mode):
create table <T> with pk [<col>:<type>...]
drop table <T>
add column [to] [table] <T>: <col> (<type>)
(for serial/shortid on a non-empty table: existing rows auto-filled)
drop column [from] [table] <T>: <col>
rename column [in] [table] <T>: <old> to <new>
change column [in] [table] <T>: <col> (<newtype>)
[--force-conversion | --dont-convert]
(to serial/shortid: null cells auto-filled with generated values)
add 1:n relationship [as <name>] from <P>.<col> to <C>.<col>
[on delete <action>] [on update <action>] [--create-fk]
drop relationship <name>
insert into <T> [(cols)] [values] (vals)
update <T> set <c>=<v>... where <c>=<v> | --all-rows
delete from <T> where <c>=<v> | --all-rows
show table <T>
show data <T>
replay <path> — run each non-blank, non-`#`-comment line
of <path> as a command. Stops at the first
error (no rollback). Relative paths resolve
under the current project's directory.
Types: text, int, real, decimal, bool, date, datetime, blob, serial, shortid
Auto-generated types (serial, shortid):
serial — integer that auto-fills with the next sequence value
(MAX(col)+1) on insert. Outside a primary key it carries
a UNIQUE contract.
shortid — short base58 identifier auto-filled at insert time. Always
carries a UNIQUE contract.
Adding or changing-to either type on a non-empty table auto-fills
existing/null cells in the same operation.
# ---- DSL parse error rendering --------------------------------------
parse:
# Wrapper around chumsky's structural error message. The
@@ -179,6 +276,39 @@ project:
export_usage: "usage: export [<path>]"
import_usage: "usage: import <zip-path> [as <target>]"
import_empty_target: "import: empty target after `as`"
# Project-switch validation failures (load / save-as /
# import). Returned from the runtime as Err(String) and
# surfaced via project.switch_failed.
load_path_missing: "path `{path}` does not exist"
saveas_target_exists: "`{path}` already exists; pick a different name or remove it first"
import_zip_missing: "zip `{path}` does not exist"
# ---- DSL failure wrapper + advanced-mode placeholder + fatal --------
dsl:
# Wrapper around the friendly-error layer's translated
# message, surfaced as `"<verb> <subject>" failed: <rendered>`.
failed: '"{verb} {subject}" failed: {rendered}'
# Echo line `running: <input>` shown above each command's
# response so the user has on-screen context for the
# output that follows.
running: "running: {input}"
# ---- Advanced-mode placeholder until SQL parser lands (Q1) ----------
advanced_mode:
not_implemented: "advanced mode SQL not implemented yet — echo: {input}"
# ---- Persistence-fatal banner (ADR-0015 §8) -------------------------
fatal:
persistence: "FATAL: failed to {operation} `{path}` — {message}. Quitting; investigate and restart."
# ---- Modal labels (load picker, rebuild confirm, save-as path) ------
modal:
rebuild_cancelled: "rebuild cancelled"
load_cancelled: "load cancelled"
generic_cancelled: "{title} cancelled"
path_entry_empty_name: "path entry: empty name"
path_entry_empty_path: "path entry: empty path"
load_picker_nothing: "nothing to load"
# ---- mode / messages banners (app-level commands) -------------------
mode: