Skip to content

refactor(i18n): show the last plan usage reports through Fluent - #971

Merged
penso merged 25 commits into
mainfrom
i18n/usage-providers-a
Nov 1, 2026
Merged

penso merged 25 commits into
mainfrom
i18n/usage-providers-a

Conversation

@penso

@penso penso commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Stacked on #970 (base i18n/usage-providers-d), then #969, #968, #967, and #955. Merge those first.

Summary

The last part of the herdr-usage report words in Phase 2 of docs/i18n.md, following #967. This PR covers the remaining 19 providers: Abacus, AI& (aiand), Aixy, Alibaba, the Alibaba token plan (which Qwen Cloud shares), Amp, Antigravity, AtlasCloud, Augment, Azure OpenAI, Bedrock, Bifrost, Chutes, Claude (and claude-swap's other accounts), ClawRouter, Codebuff, CodeRabbit, Codex, and Command Code. Cline Pass's report has no words.

  • Messages: each provider names its report's words in its own usage-<provider>.ftl. usage-claude.ftl and usage-codex.ftl are new. Plan and product names ("Amp Free", Auggie's plans, Codex and Claude plans, Command Code's tiers, Alibaba's editions) and the services' own words (CodeRabbit's inactive, Chutes' units) stay as they send them.

  • Composed window names: Antigravity's windows use one message with a $cadence selector, so they still sort by group first. Bifrost names its windows from nested messages (scope · budget · period).

  • The transitional conversions go: with every provider naming its words, Text's From<&str>/From<String> and the pair/pairs/share/shares helpers from refactor(i18n): name plan usage report words as typed text #967 are deleted. A report can no longer carry an English literal: each word is either a message or Text::service(..), and anything else fails to compile. The crate's and the app's test fixtures build their words that way.

  • Counts: where the old English never had a singular ("12 routed providers", "{used} / {total} credits"), it stays byte for byte.

  • Tests: herdr-usage tests assert message IDs and arguments, including the shared Claude fixture in tests/provider_data.rs. usage/tests/words_a.rs checks that each provider's English reads as it wrote it.

  • docs/i18n.md is left as main has it: one later change updates its "What is left" list once the open i18n PRs merge.

Screenshots

None for this PR: these providers need real sign-ins, which the isolated sandbox doesn't have. #967 shows the same panel and section code with a provider whose report is fully named.

QA

  • just format
  • just ci on a8b52be20 (macOS arm64): passed, with its base and origin/main merged in
  • herdr-usage tests (429) and the app's usage tests (104), with no From<&str> for Text left
  • words_a.rs: each provider's English reads as before
  • Report message scan: every ID across all providers exists with exactly its chained arguments; just check-i18n: 4645 messages in 248 files valid and used
  • With a signed-in Claude or Codex account under en-XA: the panel's sections (Claude's "This week by surface" and "Extra usage", Codex's credits) are bracketed, and plan names stay as sent
  • English: these providers' panels read as before

🤖 Generated with Claude Code

https://claude.ai/code/session_01BFavh7vQNk6CpU1rxrJkHw

penso added 7 commits October 31, 2026 21:12
Each usage provider's settings carried their help as English in
herdr-usage, which the usage panel's Setup block showed as written.

Setting::help becomes Setting::message, the ID of a message in the
provider's own locales/en-US/usage-<id>.ftl (156 messages across the
providers that read settings). The panel shows it with i18n::message.
One file per provider keeps parallel provider PRs out of each other's
catalogs.

The example config's generated provider section came from the English in
herdr-usage. Its generator and bless test move to the app's usage tests,
which read the English messages. The section is unchanged byte for byte.
A new test checks that every setting names a message without arguments,
and the en-XA leak test no longer allows the help as data.
herdr-usage reports carried English Strings for section titles, fact
labels and values, share labels, balance labels, window names, and the
account line, and count units as English words, which the usage panel
showed whatever the language.

herdr_usage::Text is now the words a report shows: a message ID with
arguments, which the app writes with usage::text::words, or the service's
own words. Count units are a typed Count the app writes with a plural
(usage-amount-*). Wayfinder names all of its words in usage-wayfinder.ftl;
the other providers keep their English through a transitional
From<&str>/From<String> for Text and pair/pairs/share/shares helpers,
which go once the last of them names its words.

A crate below the app cannot use fl!'s compile-time check, so an app test
scans herdr-usage for Text::message IDs and the arguments chained to
them and checks each against the English messages. The en-XA leak test
now draws facts, shares, and a credits balance a provider names.
A fact's value sat at its natural width, so a longer one, as en-XA or
another language writes it, ran past the usage card's edge. The value
may now shrink and wraps under itself, set against the end side.
…Fluent

Copilot, Cursor, Deepgram, DeepInfra, DeepSeek, Devin, DevPass, Doubao,
ElevenLabs, Factory, Fireworks, Gemini, GitKraken, Grok, Groq, Helmcode,
Hugging Face, Hyper, IBM Bob, JetBrains, Kilo, and Kimi now name each
word their reports show in their own usage-<provider>.ftl: section
titles, fact labels, values with English words or plurals, balance
labels, the windows they name, and plan lines they compose. Names the
services send (plans, models, tenants, products) stay as the service's
words, and none of them uses Text's transitional conversions.

Where a provider matched its own English labels (Cursor's on-demand
budget, Doubao's window periods), it now carries a selector key the
message picks by. Counts whose English never had a singular ("1
credits") keep it byte for byte, with only an other variant for
translators to extend. usage/tests/words_b.rs checks each provider's
messages read in English as it wrote them.
…gh Fluent

Kiro, LiteLLM, llmman, LLM Proxy, LongCat, Manus, MiMo, MiniMax,
Mistral, Moonshot, Muse, Neuralwatt, Notion, Nous, Ollama, OpenAI,
OpenCode, OpenCode Go, and OpenRouter now name each word their reports
show in their own usage-<provider>.ftl. Plans, models, product names
(Vibe, Notion AI, Ollama Cloud), and the services' own status words stay
as the service's words. OneConsole is shared gateway code and shows
none.

OpenAI's unnamed line items and models are now None rather than English
fallback keys, and show through messages. LiteLLM's spend parser takes a
Reporter enum instead of matching its own label. MiMo, Neuralwatt, and
Muse counts gain a singular ("1 credit" where it said "1 credits").

Named windows sort by their words; message IDs are chosen so each
provider's windows keep their order, which its tests hold, and
Report::new documents it. usage/tests/words_c.rs checks each provider's
English reads as it wrote it.
…uent

Perplexity, Poe, Qoder, Raycast, Replicate, Sakana, StepFun, Sub2API,
Synthetic, T3 Chat, Typesafe, v0, Venice, Vercel, Vertex AI, Warp,
Windsurf, xAI, xKiro, Z.ai, Zed, ZenMux, and ZoomMate now name each word
their reports show in their own usage-<provider>.ftl. Plan names the
providers map themselves, currencies, and tool and protocol names stay
as written. Qwen Cloud's words come from the Alibaba token plan code,
which the last batch moves, so its test no longer pins them. Pi never
reports.

Poe's points, Venice's credits, Warp's credits, and ZenMux's flows gain
a singular. Synthetic gives its hourly length only to the search slot's
own window, not any quota the service names "Search hourly". Z.ai picks
its model detail by a selector, and ZenMux names its session and weekly
facts rather than borrowing Kind::title's English.
usage/tests/words_d.rs checks each provider's English reads as it wrote
it.
Abacus, AI& (aiand), Aixy, Alibaba, the Alibaba token plan, Amp,
Antigravity, AtlasCloud, Augment, Azure OpenAI, Bedrock, Bifrost,
Chutes, Claude (and claude-swap's accounts), ClawRouter, Codebuff,
CodeRabbit, Codex, and Command Code now name each word their reports
show in their own usage-<provider>.ftl. Plan and product names and the
services' own words stay as they send them. Cline Pass shows none.

With every provider naming its words, Text's transitional
From<&str>/From<String> and the pair/pairs/share/shares helpers go, so a
report can no longer carry an English literal: it must be a message or
Text::service. The crate's and the app's tests build their fixtures
that way.
usage/tests/words_a.rs checks each provider's English reads as it wrote
it, and docs/i18n.md moves plan usage's reports out of "What is left".
@penso

penso commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator Author

just ci ran locally and passed on a8b52be20 (macOS arm64).

@greptile-apps

greptile-apps Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no blocking issue was found.

Summary

Moves the remaining 19 providers’ report text into Fluent messages while keeping service names and values unchanged.

  • Usage reports from the remaining providers can show translated labels and phrases.
  • Usage report text must now say whether its words come from Fluent or a service.

Reviews (4) · Last reviewed commit: "Merge branch 'i18n/usage-providers-d' in..." · Reviewed by Greptile

penso added 5 commits November 1, 2026 09:49
Named windows became message IDs in this branch, and the usage command
labeled each window with `Kind::title`, which gives a named window's ID:
it printed usage-copilot-premium instead of Premium, and Doubao's window
lost its plan and period, which only the message's arguments carry. The
command now writes a named window through `usage::text::english`, and a
test checks the labels and the JSON the command answers.
Z.ai's token totals sorted ties by cloned model names; the sort key now
borrows them, with an unnamed model still sorting as Unknown did.

Synthetic's search lane runs an hour because it is the dedicated lane,
which now holds a message, so a generic quota the service names "Search
hourly" is the service's own name and keeps no length. A test pins that.
@penso

penso commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator Author

@greptileai review

penso added 7 commits November 1, 2026 11:02
Every open i18n pull request edits the remaining list in docs/i18n.md, so
each merge made the others conflict. This branch now keeps main's copy;
one change updates the list once they have all merged.
@penso

penso commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator Author

@greptileai review

@penso

penso commented Nov 1, 2026

Copy link
Copy Markdown
Collaborator Author

@greptileai review

Base automatically changed from i18n/usage-providers-d to main November 1, 2026 21:45
@netlify

netlify Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for herdr-ui canceled.

Name Link
🔨 Latest commit 5afab2d
🔍 Latest deploy log https://app.netlify.com/projects/herdr-ui/deploys/6ae7b3118248ec41addb6065

@penso
penso merged commit 94489be into main Nov 1, 2026
4 checks passed
@penso
penso deleted the i18n/usage-providers-a branch November 1, 2026 21:46
shin1ohno added a commit to shin1ohno/herdr-gpui that referenced this pull request Nov 3, 2026
Brings in upstream's 30 commits, among them side-by-side agent chats in
Herdr 2 panes (sheprd-labs#935), choosing the agent a new worktree opens in (sheprd-labs#948),
the Vulkan driver hint (sheprd-labs#977), readable branch divergence counts (sheprd-labs#999),
and the next round of i18n Fluent migrations with typed updater reasons
(sheprd-labs#951-sheprd-labs#971, sheprd-labs#989).

Conflicts: upstream's code wins and the iPad's needs are re-applied on
top. Tests that moved into their own files keep this branch's iOS gates.
The core terminal state keeps both upstream's pending spawn and the
iPad's app-owned SSH workspace, and the iPad focus frame wraps
upstream's pane body. The device picker keeps reading Herdr 2 state from
the window on iPad and otherwise shows upstream's localized status. The
updater stays disabled on iPadOS with upstream's Platform reason, the
update panel keeps its iPadOS wording, and plan usage and the
orchestrator stay off on iPad.

Beyond the conflicts, upstream's changes needed these to build and pass
on iPadOS:
- Upstream marked app.rs and config.rs as i18n-migrated, which covers
  app/ios.rs and config/ipad_storage.rs. The theme load failure and the
  oversized config error get Fluent messages; debug names, the
  background task name and the diagnostics fallback are marked as data.
- config.rs stays under 1,000 lines. The iPad migration warning and the
  platform read function move into ipad_storage.rs and files.rs.
- The iPad startup loads and installs the remembered worktree landing,
  as the desktop's does since sheprd-labs#948.
- Helpers only the desktop's control server and link handling use are
  compiled out on iOS: the usage English text, the window's link
  complaint, and, outside tests, Endpoint::status and
  focused_core_chat_key. Error and test modules for desktop-only link
  and checkout code are compiled out too.
- The iPad sidebar test's chat target names its surface, and the
  updater's reason test expects the iPadOS reasons on iOS.
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