feat(i18n): extract core_commands.py user-facing strings#652
Conversation
thomwebb
left a comment
There was a problem hiding this comment.
Adversarial Review — Automated (code-puppy-4aeaa3)
Verdict: CONDITIONAL PASS — mechanical extraction is correct, no crash vectors. Three issues before merge.
Must Fix
1. Key reuse: cmd.agent.picker_failed used inside handle_model_command
core_commands.py:488 (approx) — the model command's picker-failure path reuses cmd.agent.picker_failed. If a translator ever needs to word these differently (agent picker vs model picker), there's no seam. Create cmd.model.picker_failed in en-US.json and use it in handle_model_command.
2. Test namespace floor too loose
tests/i18n/test_core_commands_i18n.py — assert len(_cmd_keys()) >= 25 allows 6 keys to silently vanish. The PR adds 31 keys; floor should be >= 31.
3. No targeted interpolation test for cmd.add_model.failed
The key has an {error} placeholder. test_no_leftover_placeholder_for_supplied_params catches this generically with "X", but a param-name typo in the catalog would still pass. Add one explicit assert.
Track / Follow-up (not merge blockers)
Text.from_markup("[yellow]Usage:[/yellow] /agent <agent-name>")near line 384 is content-identical tocmd.agent.usagebut bypassest()— will drift if a translator updates the catalog key"No description"fallback (line 371) is user-visible, still raw"Failed" in resultdispatch in the undo handler will silently break ifundo_manager.pyever gets localized — track in a ticket- Add
# i18n: skip - developer outputcomments on the twoTraceback:f-string lines so future auditors don't re-flag them
Everything else checks out: import is at module level, all 31 keys exist in en-US.json, all param names match, t() is crash-safe (missing key returns key, missing param leaves placeholder intact), no Rich markup in catalog values.
thomwebb
left a comment
There was a problem hiding this comment.
Review feedback addressed — commit 3b67f7ed
Automated fix pass (code-puppy-4aeaa3). Applied the three "must fix" items:
Landed:
- Added new key
cmd.model.picker_failed("Model picker failed to open: {error}");handle_model_commandno longer reusescmd.agent.picker_failed - Test floor tightened:
>= 25→>= 32(matches actual current key count) - Added
test_add_model_failed_interpolates_error_param— explicit interpolation check that catches param-name typos independently of the generic sweep test - Also added the new
cmd.model.picker_failedkey totest_model_keys_interpolate
Verification:
pytest tests/i18n/test_core_commands_i18n.py -q→ 11 passedruff check --fix/ruff format→ clean- Catalog JSON parses; 32
cmd.*keys
Deferred (track as follow-up, not blocking):
Text.from_markup("[yellow]Usage:[/yellow] /agent <agent-name>")at ~line 384 duplicatescmd.agent.usagecontent but bypassest()"No description"fallback at line 371 still raw"Failed" in resultdispatch in undo handler will break ifundo_manager.pygets localizedTraceback:f-string lines still need# i18n: skip - developer outputcomments
CI should re-run on the new commit.
Migrate 41 user-facing emit_* strings across 7 command handlers (/cd, /paste, /tutorial, /exit, /agent, /model, /add_model, /model_settings, /plan) to the t() catalog. - /cd: directory-change success/error, agent-context reload (cmd.cd.*) - /paste: clipboard image flow, count, hints (cmd.paste.*) - /tutorial: OAuth flow start, completion, skip notices (cmd.tutorial.*) - /exit: reuses existing cli.goodbye key (no new key) - /agent: already-using, switch-failed, switched, cancelled, picker-failed, usage (cmd.agent.*); not-found reuses cfg.agent.not_found, available-list reuses cli.agent.available - /model: success, cancelled, usage, available (cmd.model.*); picker-failed reuses cmd.agent.picker_failed - /add_model: success, failed (cmd.add_model.*) - /model_settings: success, agent-reloaded, reload-failed, failed (cmd.model_settings.*) - /plan: usage error (cmd.plan.usage) Adds 31 cmd.* keys; 47 raw sites -> 6 (all 6 remaining are false positives: Rich-markup f-strings in fallback paths, traceback diagnostics, and pure-variable agent description strings). Also renames the loop variable 't' -> 'token' in handle_generate_pr_description_command to avoid shadowing the module-level t() import (caught by ruff F402). Tests: tests/i18n/test_core_commands_i18n.py (10 tests). All 128 i18n tests pass. ruff clean.
- Add distinct cmd.model.picker_failed key (was incorrectly reusing
cmd.agent.picker_failed in handle_model_command fallback path).
- Point the model-picker fallback emit_warning at the new key so the
displayed message matches the actual subsystem that failed.
- Tighten the cmd.* namespace floor test to >= 32 (the true post-
extraction count) so accidental key removals fail fast instead of
silently shrinking the catalog.
- Add explicit interpolation test for cmd.add_model.failed that
substitutes error='XYZ' and asserts the literal '{error}' token is
gone -- catches placeholder-name typos independently of the generic
sweep.
The '--show' branch of handle_model_settings_command used 'for t in tokens[1:]' which shadowed the module-level 't' translation import for the entire function scope. This caused UnboundLocalError on every subsequent t() call in the non-show branch. Renamed loop variable to 'token'.
Upstream commit f0dab86 added reasoning_context and reasoning_mode to supported_settings for gpt-5.6* models but did not update this test. Split the expected list so gpt-5.6* variants get the extra fields. This piggyback will drop from the branch once a standalone upstream fix lands.
584d76e to
0342aff
Compare
thomwebb
left a comment
There was a problem hiding this comment.
Rebased + piggyback test fix
Rebased onto latest main (now at f0dab866) and added a small piggyback commit fixing an unrelated broken test that CI was tripping over.
Context
Upstream commit f0dab866 (feat: add GPT-5.6 reasoning context and mode) added reasoning_context and reasoning_mode to supported_settings for gpt-5.6* models in chatgpt_oauth/utils.py but did not update tests/plugins/test_chatgpt_oauth_utils.py::test_add_models_to_extra_config_gpt54_and_newer_support_xhigh, which asserts a hardcoded 3-item list. Result: main's own Build and Publish to PyPI run is failing for the same reason.
What the piggyback commit does
Splits the assertion so codex-gpt-5.6-* variants expect the extra fields:
expected_settings = ["reasoning_effort", "summary", "verbosity"]
if model_name.startswith("codex-gpt-5.6"):
expected_settings.extend(["reasoning_context", "reasoning_mode"])
assert model_config["supported_settings"] == expected_settingsVerified locally: test passes on this branch.
Cleanup
This commit is a piggyback and will drop from the branch automatically once the same fix lands on upstream main via a rebase.
What
Migrates 41 user-facing
emit_*strings across 7 slash-command handlers incommand_line/core_commands.pyto thet()catalog — the #2 extraction target at 47 raw audit sites (41 genuinely translatable; 6 false positives).Result
47 raw → 6 — all remaining hits are false positives:
Text.from_markup(f"[dim]...{var}...[/dim]"))f"Traceback: {traceback.format_exc()}")f"{new_agent.description}")31 new
cmd.*keys added toen-US.json; 5 reuse existing keys:cli.goodbye/exitgoodbye messagecfg.agent.not_foundcli.agent.availablecmd.agent.picker_failedKey groupings
cmd.cd.*/cd: success, list-error, agent-updated, reload-error, not-a-dircmd.paste.*/paste: no-image, hint, count, send-hint, failedcmd.tutorial.*/tutorial: chatgpt-oauth, claude-oauth, complete, skippedcmd.agent.*/agent: already-using, switch-failed, switched, cancelled, picker-failed, usagecmd.model.*/model: success, cancelled, usage, availablecmd.add_model.*/add_model: success, failedcmd.model_settings.*/model_settings: success, agent-reloaded, reload-failed, failedcmd.plan.usage/plan: usage errorBonus fix
Renamed the loop variable
t→tokeninhandle_generate_pr_description_command— it was shadowing the module-levelt()import (caught byruff F402).Tests
tests/i18n/test_core_commands_i18n.py(10 tests): namespace populated, all keys resolve + pseudolocalize, interpolation for each command group, no leftover placeholders, module imports cleanly. All 128 existing i18n tests still pass.ruffclean.Independence
Touches only
core_commands.py,en-US.json, and the new test — no overlap with any open PR.