# en-US catalog (ADR-0019). # # Hierarchical groups flatten to dot-paths internally: # error.unique.insert.headline # error.unique.insert.hint # help.cli_banner # replay.completed # … etc. # # Each error entry has a `headline` (one line, used in both # short and verbose modes) and may have a `hint` (one or more # lines, surfaced only in verbose mode). Short mode = headline # only; verbose mode = headline + hint + (if present) the # diagnostic table the translator built (ADR-0019 §7). # # Anchor phrases per ADR-0019 §10 are kept stable across # wording changes: # "no such table" # "no such column" # "no such relationship" # "already exists" # "already has the value" # "cannot be converted" # "discard information" # "referenced by" # "[client-side]" # # Placeholders use `{name}` substitution; format specifiers # (`{name:08.2}`, `{name:>10}`, …) are explicitly rejected by # the substitution helper (ADR-0019 §8.4). # Sanity entry exercised by the loader's unit tests; not # user-facing. _test: hello: "Hello, {name}!" # ---- Error category -------------------------------------------------- error: # UNIQUE constraint violations. Anchor: "already has the value". unique: insert: headline: "`{table}.{column}` already has the value `{value}`." hint: "The `{column}` column on `{table}` is unique — pick a different value, or update the existing row instead." update: headline: "`{table}.{column}` already has the value `{value}`." hint: "The `{column}` column on `{table}` is unique — your update would create a duplicate." # Primary-key collisions get distinct wording — the user # learns that PK is the canonical unique constraint. pk: insert: headline: "`{table}` already has a row with primary key `{value}`." hint: "Primary keys must be unique — pick a different value or update the existing row." update: headline: "`{table}` already has a row with primary key `{value}`." hint: "Primary keys must be unique — your update would create a duplicate." # FOREIGN KEY violations. Anchor: "referenced by". foreign_key: # Child-side: insert/update sets a value that has no parent. child_side: insert: headline: "no parent row in `{parent_table}` has `{parent_column}` = `{value}`." hint: "Foreign keys must point at an existing parent row. Insert a matching parent first, or pick a value that already exists in `{parent_table}.{parent_column}`." update: headline: "no parent row in `{parent_table}` has `{parent_column}` = `{value}`." hint: "Foreign keys must point at an existing parent row. Pick a value that already exists in `{parent_table}.{parent_column}`." # Parent-side: delete/update on a row referenced by children. # The engine refuses unless the relationship's `on delete / # on update` says cascade or set null. parent_side: delete: headline: "`{table}` rows are referenced by `{child_table}`." hint: "Deleting these rows would orphan the children. Delete the children first, or change the relationship's `on delete` action to `cascade` or `set null`." update: headline: "`{table}` rows are referenced by `{child_table}`." hint: "Updating the referenced column would orphan the children. Update the children first, or change the relationship's `on update` action to `cascade`." # NOT NULL constraint violations. not_null: insert: headline: "`{table}.{column}` cannot be null." hint: "The `{column}` column is required — provide a value for it in the row you are inserting." update: headline: "`{table}.{column}` cannot be null." hint: "The `{column}` column is required — pick a non-null value, or do not include `{column}` in your `set` list." # CHECK constraint violations. Placeholder coverage — # the playground does not emit CHECK constraints today # (track C3), but the catalog is wired so the wording # is ready when constraint-management lands. check: insert: headline: "check constraint refused `{table}.{column}`." hint: "A check constraint requires `{column}` to satisfy a rule the inserted value did not." update: headline: "check constraint refused `{table}.{column}`." hint: "A check constraint requires `{column}` to satisfy a rule the new value did not." # Type mismatch — engine-side STRICT refusal of a wrong-shape # value. Mostly the `change column ... --dont-convert` path # today (subsumes `friendly_change_column_engine_error`). type_mismatch: change_column: headline: "cannot change `{table}.{column}` from `{src_type}` to `{target_type}` with `--dont-convert`." hint: "The database refused at least one cell as the wrong shape for `{target_type}`. Re-run without `--dont-convert` to see which rows." insert: headline: "value `{value}` is not a `{expected_type}`." hint: "The `{column}` column on `{table}` is `{expected_type}` — provide a `{expected_type}` value, or change the column's type with `change column`." update: headline: "value `{value}` is not a `{expected_type}`." hint: "The `{column}` column on `{table}` is `{expected_type}` — provide a `{expected_type}` value, or change the column's type with `change column`." # Object-not-found errors. Anchor: "no such ...". These are # genuinely single-line errors — no hint adds value. not_found: table: headline: "no such table: `{name}`" column: headline: "no such column: `{table}.{column}`" column_unqualified: headline: "no such column: `{column}`" relationship: headline: "no such relationship: `{name}`" # Name-collision errors. Anchor: "already exists". already_exists: table: headline: "table `{name}` already exists" column: headline: "column `{table}.{column}` already exists" relationship: headline: "relationship `{name}` already exists" # Generic catch-all when the translator can't classify the # engine error into a known category. The wording stays # engine-neutral; the message text from the engine is NOT # surfaced (ADR-0002 user-facing posture). generic: headline: "the database refused this `{operation}`." hint: "The operation could not be completed against the current state of `{table}`." # Errors that are specifically about value validation # (DbError::InvalidValue) — wrong arity, wrong literal # form, etc. Pre-engine; the catalog covers what the # parser / validator already decided was wrong. invalid_value: arity: headline: "expected {expected} value(s), got {actual}." empty_insert: headline: "INSERT requires at least one column value." empty_update: headline: "UPDATE requires at least one assignment." # ---- Help text (CLI banner + in-app `help` command) ------------------ # ---- CLI argument-parsing errors (stderr before TUI starts) --------- cli: missing_value: "missing value for --{flag}" invalid_value: "invalid value for --{flag}: {value} (expected one of: {expected})" unknown_argument: "unknown argument: {arg}" multiple_paths: "only one project path may be supplied; got both `{first}` and `{second}`" resume_with_path: |- --resume and a positional are mutually exclusive; pass one or the other 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] [] Arguments: 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 Override theme (default: auto-detect). --data-dir Use PATH as the data root instead of the OS-standard location for this run. --log-file Write tracing output to PATH. --resume Open the most-recently-used project (path tracked under /last_project). Errors out if no previous project is recorded. Mutually exclusive with . App-level commands (typed inside the app, available in both modes): quit 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 [] Write a zip of project.yaml + data/ to (relative paths under the data root). Excludes playground.db and history.log. import [as ] Unpack into a new project and switch to it. overrides the target name (else taken from the zip). # In-app `help` command output (ADR-0024 §help_id). The # renderer iterates the command REGISTRY and translates each # CommandNode's `help_id` — so a newly-registered command # appears in `help` automatically, with no edit here. `intro` # and `dsl_section` frame the two command groups; each # `app.*` / `ddl.*` / `data.*` entry is keyed by a command's # `help_id`; `types_reference` closes the block. All entries # are multi-line-capable — the renderer emits one output row # per line so scroll math stays accurate. intro: "Supported commands:" dsl_section: "DSL data commands (in simple mode):" # Per-command help, keyed by `CommandNode.help_id`. Block # scalars (`|-`) so the column alignment survives — the # double-quoted form trips a libyml scanner bug on long # internal space runs. The renderer emits one output row per # line, so multi-form commands list each form on its own line. app: quit: |- quit — exit the app help: |- help — show this command list rebuild: |- rebuild — rebuild the project database from project.yaml + data/ (with confirmation) save: |- save — save the current temp project under a name save as — copy the current project to a new name/path new: |- new — close the current project, start a fresh temp project load: |- load — open the project picker export: |- export [] — write a zip of project.yaml + data/ (excludes the database file and history.log) import: |- import [as ] — unpack a zip into a new project and switch to it mode: |- mode simple|advanced — switch input mode messages: |- messages [short|verbose] — show or switch error-message verbosity (verbose is the default) ddl: create: |- create table with pk [(), ...] — create a table drop: |- drop table — remove a table drop column [from] [table] : [--cascade] — remove a column (--cascade also drops any index that covers the column) drop relationship — remove a relationship drop index — remove an index drop index on (, ...) — remove an index by its columns add: |- add column [to] [table] : () — add a column (for serial/shortid on a non-empty table: existing rows auto-filled) add 1:n relationship [as ] from

. to . [on delete ] [on update ] [--create-fk] — declare a relationship add index [as ] on (, ...) — create an index rename: |- rename column [in] [table] : to — rename a column change: |- change column [in] [table] : () [--force-conversion | --dont-convert] — change a column's type (to serial/shortid: null cells auto-filled with generated values) data: show: |- show table — show a table's structure show data — show a table's rows insert: |- insert into [(cols)] [values] (vals) — add a row update: |- update set =, ... where = | --all-rows — change matching rows delete: |- delete from where = | --all-rows — remove matching rows replay: |- replay — run each non-blank, non-`#`-comment line of as a command. Stops at the first error (no rollback); relative paths resolve under the project directory. explain: |- explain show data | explain update ... | explain delete from ... — show how the database would run a query, without running it (safe even for update / delete) # Type reference, appended after the command list. types_reference: | 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 -------------------------------------- # ---- Hint panel ambient typing assistance (ADR-0022 §6) ------------- hint: # The hint panel goes ambient as soon as the user types # anything — empty input keeps the existing # `panel.hint_empty` content. # Hint sentences are full standalone phrases, capitalised # at the start. Inline `{message}` substitutions inherit # whatever case the source produced (parser errors, # engine messages) — they're embedded mid-sentence so they # stay lowercase by convention. ambient_complete: "Submit with Enter" ambient_expected: "Next: {expected}" ambient_error_with_usage: "{message} — usage: {usage}" # Invalid identifier in a schema slot (ADR-0022 stage 8e # + the user's #5). Voice mirrors ADR-0019's "no such # {kind}" wording for consistency with engine errors. ambient_invalid_ident: "No such {kind}: `{found}`" # User-invented-name slot (NewName per IdentSlot). The # probe-derived `{next}` is what comes after the name — # e.g. `(` after a new column name. Empty/unknown `next` # falls through to `ambient_typing_name` instead. ambient_typing_name: "Type a name" ambient_typing_name_then: "Type a name, then {next}" # Value-literal slot — `insert ... values (`, `update ... set # col=`, `where col=`. Replaces the misleading "null true # false" keyword candidate list with format guidance for all # accepted literal forms. Used when the walker can't resolve a # column type (schemaless parse, missing table, unknown column). value_literal_slot: "Type a value: number, 'text', true/false, null (dates as 'YYYY-MM-DD', datetimes as 'YYYY-MM-DDTHH:MM:SS')" # Per-column-type value-slot hints (ADR-0024 §Phase D §typed-value-slots). # Fired when the walker resolved the column's user-facing type # at the current value slot; narrows the prose to the relevant # literal forms for that type. Falls back to # `value_literal_slot` when the type can't be resolved. value_slot_int: "Type an integer (e.g. 42, -7) or null" value_slot_real: "Type a number (e.g. 3.14, -0.5) or null" value_slot_decimal: "Type a number (e.g. 19.95, -2.50) or null" value_slot_bool: "Type true, false, or null" value_slot_text: "Type a quoted string (e.g. 'Alice') or null" value_slot_date: "Type a quoted date as 'YYYY-MM-DD' or null" value_slot_datetime: "Type a quoted datetime as 'YYYY-MM-DD HH:MM:SS' or null" value_slot_blob: "Type a quoted blob literal or null" # Serial / shortid in `values (…)` form: the user must enter # something at this position (no "skip column" syntax). `null` # is the auto-fill path (ADR-0018: serial / shortid columns # auto-fill null cells on insert), so the prose leads with # null and offers an explicit value as the alternative. value_slot_serial: "Type null to auto-generate, or an explicit integer" value_slot_shortid: "Type null to auto-generate, or a quoted shortid" # Wrapper that prefixes a per-column-type slot hint with the # actual column name so the user sees "for `Email`: Type a # quoted string …" instead of the generic type prose. value_slot_for_column: "for `{column}`: {detail}" # Pedagogical note appended at the first value slot of a # Form B `insert into T values (…)` when T has auto-generated # columns the value list skips — points the user at the # explicit-column form so the skipped column is discoverable. value_slot_autogen_skipped: "({columns} auto-generated — skipped here; list columns explicitly, e.g. `insert into T (...) values (...)`, to set it.)" parse: # Wrapper around chumsky's structural error message. The # caret pointer (visualising the failure column) is printed # on its own preceding line via `parse.caret`. error: "parse error: {detail}" # Wrapper used by `ParseError::Display` (so any to_string() # call on a parse error renders consistently with the in-app # error rendering). error_wrapper: "could not parse command: {detail}" # Custom (try_map / source-slice) error messages raised by # the DSL parser. These were hand-written strings in # `src/dsl/parser.rs` until the catalog migration brought # them under the same roof as the rest of the user-facing # vocabulary. Wording is unchanged from the inline source # form so existing anchor-phrase tests still match. custom: replay_path_expected: "expected a path after `replay`" create_table_needs_pk: |- tables need at least one column. Add `with pk` for a default `id INTEGER PRIMARY KEY`, or `with pk ()` to choose. Use a comma-separated list for compound primary keys. # ADR-0026 §1: the recursion-depth guard on the # WHERE-expression grammar. Input nested past the cap # (`((((…))))`) stops here with a friendly error instead # of overflowing the parser stack. expression_too_deep: "expression nested too deeply" on_action_specified_twice: "`on {target}` specified twice" change_column_flags_exclusive: "`--force-conversion` and `--dont-convert` are mutually exclusive — pick one." unknown_type: "unknown type '{found}' (expected one of: {expected})" unknown_action: "unknown referential action '{found}' (expected one of: {expected})" # Phase D typed-value-slot mismatch (ADR-0024 §Phase D): # surfaced when a column's value slot rejects the literal # the user typed (e.g. `3.14` at an `int` column). `{found}` # is the literal text; `{expected}` names the required # shape (`integer`, `number`, …). bind_type_mismatch: "value '{found}' is not a valid {expected}" # `insert into T (col)` with no `values (...)` afterwards # — the shared `(…)` opener matched as a Form-A column # list but the user hasn't typed the value clause yet. # Surfaced as a parse-time error so the input renderer # classifies the input as mid-typing rather than # dispatching a logically-empty Form C insert. insert_form_a_missing_values: "`insert into ...({columns})` looks like Form A — add `values (...)` to supply the matching values." # ADR-0029 §9: a primary-key column is already NOT NULL, # and a single-column primary key is already UNIQUE — # declaring either explicitly is redundant. constraint_redundant_on_pk: "`{column}` is a primary-key column, so it is already {constraint} — drop the redundant constraint." # Caret pointer showing where in the input the parser # failed. `{padding}` is the leading whitespace; the # template appends `^` so the rendered line places the # marker under the offending character. caret: "{padding}^" # Default for the `ParseError::Empty` variant — surfaces as # `{detail}` inside the wrapper. empty: "empty input" # No-prefix fallback (ADR-0021 §5): when the parse fails # before any keyword is consumed, the renderer lists every # command-entry keyword instead of attempting a per-command # usage block. `{commands}` is an oxford-joined list of # command-keyword renderings (each from # `parse.token.keyword.*`). available_commands: "available commands: {commands}" # Per-command usage templates (ADR-0021 §1). Rendered under a # "usage:" prefix when a parse fails after consuming a # known command-entry keyword. The bracket convention `[...]` # marks optional parts; angle-bracket `<...>` marks # placeholders. ADR-0009's surface conventions apply. usage: create_table: "create table with pk [()[, ...]]" drop_table: "drop table " drop_column: "drop column [from] [table] : " drop_relationship: |- drop relationship drop relationship from .to .drop_index: |- drop index drop index on
([, ...]) add_column: "add column [to] [table]
: ()" add_relationship: |- add 1:n relationship [as ] from .to .[on delete ] [on update ] [--create-fk] add_index: "add index [as ] on
([, ...])" rename_column: "rename column [in] [table]
: to " change_column: |- change column [in] [table]
: () [--force-conversion | --dont-convert] show_data: "show data
" show_table: "show table
" insert: "insert into
[([, ...])] [values] ([, ...])" update: "update
set =[, ...] (where = | --all-rows)" delete: "delete from
(where = | --all-rows)" explain: |- explain show data
[where ] [limit ] explain update
set =[, ...] (where | --all-rows) explain delete from
(where | --all-rows) replay: "replay | replay ''" # App-lifecycle commands (per ADR-0003, surfaced through # the parser so they participate in usage templates + # completion). Templates here describe the surface # grammar that the parser accepts; the in-app `help` # listing in `help.in_app_body` carries the user-facing # description. quit: "quit" help: "help" rebuild: "rebuild" save: "save | save as" new: "new" load: "load" export: "export []" import: "import [as ]" mode: "mode simple | mode advanced" messages: "messages | messages short | messages verbose" # ---- Pre-submit diagnostics (ADR-0027) ------------------------------- # Surfaced by the validity indicator and the hint panel before # a command is submitted, so a learner sees the problem without # having to run the command and read the engine's error. diagnostic: unknown_table: "no such table: `{name}`" unknown_column: "no such column `{name}` on table `{table}`" # WARNING-severity flags (ADR-0026 §7) — the command still # runs, but the comparison is very likely not intended. type_mismatch: "`{column}` is {type} — this comparison uses a value of a different type" eq_null: "`= NULL` is never true — use `IS NULL` or `IS NOT NULL`" # ADR-0027 Amendment 1: LIKE is a text-pattern match. like_numeric: "`{column}` is {type} — `LIKE` is a text-pattern match, not a {type} comparison" # ---- Project lifecycle event notes ----------------------------------- project: rebuild_ok: "[ok] rebuild — {summary}" rebuild_failed: "rebuild failed: {error}" switched_ok: "[ok] now editing: {display_name}" switch_failed: "project switch failed: {error}" export_ok: "[ok] export — wrote {path}" export_failed: "export failed: {error}" # Usage / argument-parsing errors for app-level commands. export_usage: "usage: export []" import_usage: "usage: import [as ]" 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" # --resume CLI failures printed to stderr before the TUI # starts (ADR-0015 §7). Wording stays one line for clean # piping; the runtime prepends `rdbms-playground: ` from # the calling code itself. resume_recorded_missing: "--resume: recorded project `{path}` no longer exists" resume_no_previous: "--resume: no previous project recorded under `{data_root}`" # Project-lock errors (single-instance enforcement, ADR-0015 §6). lock: already_held: |- project is already open in another rdbms-playground process (pid {pid} on host `{hostname}`); close that process or remove `{path}` if you're sure it's not running write: "could not write lock file `{path}`: {source}" read: "could not read existing lock file `{path}`: {source}" # Temp-project name generation failures. naming: wordlist_too_small: "wordlist must contain at least 3 entries; found {count}" too_many_collisions: |- could not generate a non-colliding temp project name after {attempts} attempts # User-typed project name validation failures. user_name: empty: "project name cannot be empty" leading_dot: "project name cannot start with `.`" invalid_char: "project name cannot contain `{ch}`; use letters, digits, `-`, `_`, or `.` only" # ProjectError variants (ProjectError Display path). data_root_unavailable: |- could not determine the OS-standard data directory; pass --data-dir to override path_not_found: "project path `{path}` does not exist" not_a_project: |- path `{path}` does not look like a project directory (no `project.yaml` and no `playground.db`) already_exists: "path `{path}` already exists; pick a different name or remove it first" io: "filesystem error at `{path}`: {source}" # SafeDeleteError — surfaces in logs when temp-project cleanup # refuses to delete a path (ADR-0015 §13). safe_delete: refused: "refusing to delete `{path}`: {reason}" io: "io error on `{path}`: {source}" # ---- DSL failure wrapper + advanced-mode placeholder + fatal -------- dsl: # Wrapper around the friendly-error layer's translated # message, surfaced as `" " failed: `. failed: '"{verb} {subject}" failed: {rendered}' # Echo line `running: ` shown above each command's # response so the user has on-screen context for the # output that follows. running: "running: {input}" # ---- Value-validation errors (per-column at bind time) -------------- value: type_mismatch: "column `{column}` expects {expected_human}, got {got}" format: "column `{column}`: {message}" # ---- Archive / zip errors (export / import) ------------------------- archive: io: "io error on `{path}`: {source}" zip: "zip error on `{path}`: {message}" export_sequence_exhausted: |- could not pick an export filename for `{project}` in `{target_dir}`: all sequence numbers up to {limit} are taken import_collision_exhausted: |- destination `{path}` already exists and the auto-suffix retries (-02 through -{limit}) are also taken; use `import as ` to choose a different name invalid_zip: "zip is malformed: {detail}" not_a_project_archive: |- zip does not contain a project (no `project.yaml` under a single top-level folder) multiple_top_folders: "zip contains more than one top-level folder; refusing to extract" unsafe_entry: "zip entry `{entry}` would escape the target directory; refusing to extract" # ---- Persistence-layer errors (CSV/YAML/IO) ------------------------- # These were thiserror Display attributes pre-round-6. Most surface # only as the inner `{message}` of `fatal.persistence` or as the # wrapped detail inside `DbError::PersistenceFatal`. persistence: io: "could not {operation} `{path}`: {source}" encode: "could not encode {kind} for `{path}`: {message}" csv: empty: "CSV is empty" invalid_utf8: "invalid UTF-8 in CSV body" unterminated_quote: "unterminated quoted field" yaml: syntax: "project.yaml syntax error: {detail}" unsupported_version: "unsupported project.yaml version: {version} (expected 1)" unknown_type: "unknown user-facing column type `{raw}` for `{table}.{column}`" unknown_action: "unknown referential action `{raw}`" migrate: version_parse: "could not read version field from project.yaml: {detail}" newer_than_supported: |- project.yaml is at version {file} but this build only understands up to version {latest}; upgrade the application or restore an older project.yaml no_migrator: |- no migrator registered for version {version} (programmer error: registry latest_version disagrees with migrators length) step_failed: "migrator from v{from} to v{to} failed: {source}" bad_output: "migrator produced an unparseable result: {detail}" io: "io error during migration on `{path}`: {source}" # ---- 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" # Modal titles + body prose rendered by ui.rs. load_picker_title: "Load project" load_picker_empty: "(no projects in data directory)" load_picker_path_prompt: "Path to project directory:" rebuild_confirm_title: "Rebuild project" rebuild_confirm_prompt: "Continue?" # ---- Save / save-as command surfaces --------------------------------- save: # `save` on a named project is a no-op with a friendly hint. already_saved: "already auto-saved; use `save as` to copy to a different location" # Modal titles for `save` (on a temp) vs `save as`. title_as: "Save as" title_save: "Save" # Prompt body for the path-entry modal opened by save / save as. path_prompt: "Name (under data dir/projects) or absolute path:" # ---- Status bar (project label) and panels --------------------------- status: no_project: "(no project)" project_label: "Project: " panel: tables_title: "Tables" tables_empty: "(none yet)" hint_empty: "Type a command — press Tab for options, `help` for a list" # Panel titles for the output and hint panels (rendered inside # the rounded border, hence the leading/trailing space). output_title: "Output" hint_title: "Hint" # ---- Shortcut hints (paired with key names in the bottom bar) ------- shortcut: submit: "submit" confirm: "confirm" cancel: "cancel" yes: "yes" no: "no" load: "load" select: "select" browse_path: "browse path" back_to_list: "back to list" switch: "switch" advanced_once: "advanced once" cancel_one_shot: "cancel one-shot" quit: "quit" # ---- mode / messages banners (app-level commands) ------------------- mode: set_simple: "mode: simple" set_advanced: "mode: advanced" show_simple: "mode: simple" show_advanced: "mode: advanced" usage: "usage: mode simple | mode advanced" unknown: "unknown mode '{value}' (expected 'simple' or 'advanced')" # Labels rendered inside the input panel's border to mark the # current input mode. `label_advanced_one_shot` is shown # while a `:` one-shot is in flight from simple mode. label_simple: "SIMPLE" label_advanced: "ADVANCED" label_advanced_one_shot: "Advanced:" messages: show: "messages: {current}" set_short: "messages: short" set_verbose: "messages: verbose" unknown: "unknown messages mode '{value}' (expected 'short' or 'verbose')" # ---- Database-error fallback wording + cascade summaries ------------ db: # DbError variants — fallback Display wording for paths that # bypass the structured friendly translator (fatal banners, # plain to_string() calls). The normal path goes through # `friendly::translate_error` which routes by the `kind` # field and renders catalog wording from `error.*` instead. error: sqlite: "database error: {message}" unsupported: "operation not supported: {detail}" invalid_value: "invalid value: {detail}" persistence_fatal: "could not {operation} `{path}`: {message}" rebuild_row_failed: |- unable to load row {row_number} from `{csv_path}` into table `{table}`: {detail} worker_gone: "database worker is no longer available" io: "io error: {detail}" cascade: # Per-relationship cascade summary appended to a delete # success note. The same template handles cascade, # set-null, and restrict/no-action cases — `{action}` is # one of the three action phrases below. summary: " related: {count} row(s) {action} in `{child_table}` for relationship `{rel}` (on delete {on_delete})" action_deleted: "deleted" action_set_null: "had FK set to null" action_blocked: "blocked" # `change column ... (newtype)` dry-run diagnostics (ADR-0017). # Surface when the migration would lose information (lossy), # produce values the target type can't represent # (incompatible), or violate a uniqueness contract (collision). diagnostic: # Column headers for the diagnostic tables. header_from: "From" header_to: "To" header_reason: "Reason" header_value: "Value" header_becomes: "Becomes" header_source_rows: "Source rows ({pk_label})" header_source_values: "Source values" # Summary lines printed above each diagnostic table. lossy_summary: |- Cannot change `{table}.{column}` from {src_ty} to {target_ty}: {total} row(s) would discard information. incompatible_summary: |- Cannot change `{table}.{column}` from {src_ty} to {target_ty}: {total} row(s) cannot be converted. uniqueness_summary: |- Cannot change `{table}.{column}` from {src_ty} to {target_ty}: {total} collision(s) would violate uniqueness. # Follow-up suggestion appended to the lossy diagnostic # (only — incompatibles can't be force-overridden). force_conversion_hint: "if you want to execute this conversion in spite of the problems, re-run with `--force-conversion`." # ---- DSL command success summaries (ADR-0019 §9 sweep) -------------- ok: # Generic `[ok] ` header used for every # successful DSL command. Verbs come from `Command::verb()` # (already English keywords); subjects are the table / # relationship endpoints derived in `Command::display_subject()`. summary: "[ok] {verb} {subject}" # Per-operation row-count footers shown beneath the summary. rows_inserted: " {count} row(s) inserted" rows_updated: " {count} row(s) updated" rows_deleted: " {count} row(s) deleted" # Shown beneath a `drop column --cascade` summary, once per # index removed because it covered the dropped column. index_dropped_with_column: " also dropped index `{index}` (it covered the column)" # ---- Client-side success notes (ADR-0017 §6, ADR-0018 §9) ------------ client_side: # Per-cell transformation notice when `change column ...` rewrote # at least one stored value (storage-class change or non-identity # mapping). `lossy` variant fires under --force-conversion when # information was discarded. transformed: |- [client-side] {count} row(s) were transformed before being stored. In raw SQL this would need an explicit `CAST` or application-level code. transformed_lossy: |- [client-side] {count} row(s) transformed; {lossy} of those discarded information (lossy). In raw SQL this would need an explicit `CAST` or application-level code. # Auto-fill notice when null cells were populated by the # serial/shortid auto-generation contract (change column path). auto_fill_transition: |- [client-side] {count} null cell(s) given auto-generated {kind} values. In raw SQL this would need an explicit UPDATE to populate. # Auto-fill notice for `add column T: x (serial)` on a # non-empty table — values run 1..N. auto_fill_add_serial: |- [client-side] {count} row(s) given auto-generated serial values 1..{count}. In raw SQL this would need an explicit UPDATE to populate. # Auto-fill notice for `add column T: x (shortid)` on a # non-empty table. auto_fill_add_shortid: |- [client-side] {count} row(s) given auto-generated shortid values. In raw SQL this would need an explicit UPDATE to populate. # ---- Replay command surfaces (ADR-0019 §9 sweep) --------------------- replay: # Success summary printed when `replay ` completes # without per-line failure. completed: "[ok] replay {path} — {count} command(s) run" # File-open failure (line_number == 0 from the runtime). failed_open: "replay {path} failed: {error}" # Per-line failure header. The command echo is on a # follow-up `command_echo` line so the renderer can format # them as separate output rows. failed_at_line: "replay {path} failed at line {line_number}: {error}" # Indented echo of the offending command line, shown after # `failed_at_line` when the runtime supplied the source. command_echo: " > {command}" # Errors the runtime constructs inside ReplayFailed.error # before forwarding to App. Carried as plain text so they # compose with `failed_at_line`'s `{error}` placeholder. error_could_not_open: "could not open `{path}`: {detail}" error_parse: "parse error: {detail}" error_nested: "nested `replay` is not allowed inside a replay file"