Skip to content

fix(statusline): make cost formatting locale-independent - #21

Merged
cdeust merged 2 commits into
mainfrom
fix/statusline-cost-locale
Aug 3, 2026
Merged

fix(statusline): make cost formatting locale-independent#21
cdeust merged 2 commits into
mainfrom
fix/statusline-cost-locale

Conversation

@cdeust

@cdeust cdeust commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Summary

On a host with a comma-decimal locale (LC_NUMERIC=fr_FR.UTF-8) the statusline cost ledger reported $0. Two independent defects compound.

1. costs.sh had no locale guard (grep -c LC_NUMERIC costs.sh → 0). Its awk emitted 19,779682 and wrote that comma value into the per-session cache; downstream arithmetic coerced it to 0.

2. The render path interpolated the value into the awk program text, so LC_NUMERIC=C was applied on the wrong side — by then the comma is syntax, not data:

LC_NUMERIC=C awk "BEGIN{printf \"%.2f\",19,779682}"   ->  19.00   # two printf args
LC_ALL=C     awk -v v="19.779682" 'BEGIN{...}'        ->  19.78

Fix

  • export LC_ALL=C at the top of costs.sh and statusline-command.sh — every awk/printf emits dot decimals regardless of the user's locale. Only numeric format specifiers (%Y-%m-%d, %02d) are used elsewhere, so date output is unaffected.
  • fmt_usd (format.sh) and cost_fmt (render.sh) now pass the value via awk -v and normalise a stray comma, so a malformed value can never become program syntax.

Verification

Measured on a fr_FR.UTF-8 host, before → after:

Check Before After
costs.sh today 0 76.543286
costs.sh month 0 0 76.543286 76.543286
fmt_usd 19,779682 $19.00 $19.78
fmt_usd 19.779682 $19.78 $19.78 (unchanged)
fmt_usd 1234.5 $1.2k $1.2k (unchanged)

bash -n clean on all four files.

Upgrade note

Caches already written with comma decimals must be purged once — they regenerate with dot decimals on the next refresh:

grep -l ',' ~/.claude/statusline-costs.jsonl.* 2>/dev/null | xargs -r rm -f

Regression origin

The previous monolithic statusline-command.sh read .cost.total_cost_usd straight from the payload — a JSON number, locale-independent. The modular refactor introduced the costs.sh ledger layer, which was not locale-safe.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JMNwLEZS6SsrWMLwd1cQ6p

cdeust and others added 2 commits August 3, 2026 23:17
Under a comma-decimal locale (LC_NUMERIC=fr_FR.UTF-8) the ledger reported $0.

Two independent defects compound:

1. costs.sh had no locale guard at all, so its awk emitted "19,779682" with a
   comma and wrote that into the per-session cache. Downstream arithmetic then
   coerced it to 0 -- `costs.sh today` returned 0 and `month` returned "0 0".

2. The render path interpolated the value into the awk PROGRAM TEXT:
     LC_NUMERIC=C awk "BEGIN{printf \"%.2f\",19,779682}"  ->  19.00
   The LC_NUMERIC=C guard was on the wrong side: it fixes output formatting,
   but by then the comma is syntax and printf sees two arguments.

Fix:
- export LC_ALL=C at the top of costs.sh and statusline-command.sh so every
  awk/printf emits dot decimals regardless of the user's locale.
- fmt_usd (format.sh) and cost_fmt (render.sh) now pass the value via `awk -v`
  and normalise a stray comma, so a bad value can never become syntax.

Verified on a fr_FR.UTF-8 host: `costs.sh today` 0 -> 76.543286;
fmt_usd "19,779682" $19.00 -> $19.78; fmt_usd "19.779682" -> $19.78 (unchanged).
Existing caches written with comma decimals must be purged once; they
regenerate with dot decimals on the next refresh.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JMNwLEZS6SsrWMLwd1cQ6p
export LC_ALL=C pinned LC_CTYPE as well, and vislen() measures with ${#s}.
Under LC_CTYPE=C that counts UTF-8 bytes instead of columns, so a 3-byte
glyph read as width 3 and a 10-glyph bar as width 30 — 7 fit/vislen tests
failed in CI.

Force LC_NUMERIC=C alone, which is the only category the comma-decimal
defect needs. LC_ALL is unset first because it outranks LC_NUMERIC when
the environment sets it.

Verified: tests/statusline/test_fit_and_pace.sh 53/53 under both
LC_ALL=fr_FR.UTF-8 and LC_ALL=C; costs.sh today emits a dot decimal under
fr_FR; vislen of a 10-glyph bar is 10.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JMNwLEZS6SsrWMLwd1cQ6p
@cdeust
cdeust merged commit 961bc9d into main Aug 3, 2026
3 checks passed
@cdeust
cdeust deleted the fix/statusline-cost-locale branch August 3, 2026 22:02
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