Skip to content

feat(i18n): wire hardcoded settings surfaces (MoA, Billing, Custom Endpoints) for all locales - #74691

Open
anatolijlaptev1991-ctrl wants to merge 1 commit into
NousResearch:mainfrom
anatolijlaptev1991-ctrl:feat/i18n-wire-hardcoded-surfaces
Open

feat(i18n): wire hardcoded settings surfaces (MoA, Billing, Custom Endpoints) for all locales#74691
anatolijlaptev1991-ctrl wants to merge 1 commit into
NousResearch:mainfrom
anatolijlaptev1991-ctrl:feat/i18n-wire-hardcoded-surfaces

Conversation

@anatolijlaptev1991-ctrl

@anatolijlaptev1991-ctrl anatolijlaptev1991-ctrl commented Jul 30, 2026

Copy link
Copy Markdown

Summary

Three settings surfaces in the Desktop app contained hard-coded English strings that bypassed the i18n system entirely:

  1. Model Settings → Mixture of Agents (model-settings.tsx) — 13 literals
  2. Custom Endpoints (custom-endpoints-settings.tsx) — 28 literals
  3. Billing panel (billing/index.tsx, plans-view.tsx, current-plan-card.tsx, auto-reload-row.tsx, use-billing-state.ts, errors.ts) — 75+ literals

These surfaces rendered English for every locale, including zh and ja. This PR wires them to the i18n system with t() / translateNow() calls and adds the corresponding keys to en.ts + the Translations interface.

Locale-neutral: no ru.ts, no catalog.ts/languages.ts changes, no new locale registration. This benefits every existing and future locale equally.

Extracted from #71573 per maintainer feedback (comment) to split the generic wiring from the Russian catalog.

Changes

  • model-settings.tsx — MoA: title, description, preset, enabled, setDefault, delete, newPreset, addPreset, defaultLabel, referenceN, remove, addReference, aggregator
  • custom-endpoints-settings.tsx — all field labels, buttons, notifications (28 strings)
  • billing/index.tsx + plans-view.tsx — Billing, Plan, Payment & credits, Usage, portal, auto-refill, Confirm downgrade
  • billing/use-billing-state.ts — usage panel: subscription credits, top-up credits, monthly spend cap, does not expire, refill when low, payment method, etc. (40+ strings)
  • billing/errors.ts — error banners: card confirmation needed, monthly spend cap reached, remote spending needs approval, session logged out, admin role required, etc. (35+ strings)
  • en.ts — 257 new keys across settings.model.moa, settings.billing.*, settings.customEndpoints.*
  • types.ts — corresponding Translations interface extensions

Sweeper review (addressed)

All five issues from the hermes-sweeper review are fixed in d65de924 (rebased on main @ f3cda0ceb):

  1. Removed type fields (sayStopToEnd, findInPage, artifactCard, quickEntry, wakeWord*) — restored from main type contract. types.ts rebuilt from main, then new keys added on top.
  2. zh.ts catalog — all new keys added (flat naming: moaTitle, moaDesc, etc.) with Chinese translations or English fallback.
  3. AUTO_REFILL_GENERIC — module-level const removed; translation moved into view-building path (use-billing-state.ts), now reactive to locale changes.
  4. Remaining literalserrors.ts:48 recovery suffix, auto-reload-row.tsx:98, model-settings.tsx:1109 aria-label — all wired.
  5. Type contractzh.ts + types.ts fully consistent.

Verification (fresh, on committed d65de924):

  • tsc clean (3 tsconfigs, 0 errors)
  • ESLint clean (0 errors, 8 pre-existing warnings)
  • test:ui -- i18n 28/28 passed
  • test:ui -- billing 105/107 (2 pre-existing failures on unchanged billing-amounts.ts)

Verification

  • tsc clean on all 3 tsconfigs (renderer, electron, e2e)
  • ESLint clean
  • npm run build (vite production build) succeeds
  • Idempotent: re-running the wiring produces no-op (detect by presence of t('settings.model.moa.title'))

Relation to other PRs

This PR has no conflicts with any locale catalog.

@alt-glitch alt-glitch added type/feature New feature or request comp/desktop Electron desktop app (apps/desktop/*) area/i18n Localization, locales, translations area/billing Account usage, credit usage, billing (cross-cutting) P3 Low — cosmetic, nice to have labels Jul 30, 2026
anatolijlaptev1991-ctrl added a commit to anatolijlaptev1991-ctrl/hermes-agent that referenced this pull request Jul 30, 2026
…00% coverage

Russian locale catalog merged from two independent native-speaker sources:
- DrMaks22's hand-reviewed catalog (PR NousResearch#72250) as the primary base
- anatolijlaptev1991-ctrl's catalog with additional keys + ё-fidelity corrections

Changes:
- i18n/ru.ts: full Russian translation (defineLocale + defineFieldCopy,
  same architecture as ja.ts/ar.ts)
- i18n/types.ts: add 'ru' to Locale union type
- i18n/catalog.ts: register ru in TRANSLATIONS
- i18n/languages.ts: add 'Русский' to LOCALE_OPTIONS + locale aliases
  (ru, ru-RU, ru_ru, russian, Русский, русский)
- i18n/languages.test.ts: test cases for ru normalization

Coverage: 2755/2755 en.ts leaf keys (100%).
Locale-neutral i18n-wiring for hardcoded surfaces (MoA/Billing/Endpoints)
is tracked separately in PR NousResearch#74691.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for isolating the wiring work; the underlying issue is real on current main (model-settings.tsx:1004, custom-endpoints-settings.tsx:232, and billing/errors.ts:28-29 still render literals).

Problems

  • The added required Translations keys (apps/desktop/src/i18n/types.ts:687) have no counterparts in the unchanged full Chinese catalog (apps/desktop/src/i18n/zh.ts:5). The PR head therefore cannot meet that catalog's type contract. It also removes unrelated type members such as sayStopToEnd; current main still requires that member at types.ts:198.
  • AUTO_REFILL_GENERIC is translated once at module initialization (billing/use-billing-state.ts:604). translateNow() reads the mutable runtime locale (src/i18n/runtime.ts:54-65), so this copy does not change after the user switches locale.
  • Several literals remain in the claimed surfaces, including the recovery suffix in billing/errors.ts:48, AutoReloadRow copy at billing/auto-reload-row.tsx:98, and the MoA switch aria-label at model-settings.tsx:1109.

Suggested changes

  • Preserve existing type fields, add the new keys to every locale catalog, and translate the remaining billing/MoA literals.
  • Move the auto-refill generic translation into the view-building path and add a non-English render test.

This is an automated hermes-sweeper review.

@@ -710,6 +684,205 @@ export interface Translations {
fallbackEmpty: string
notInCatalog: string
tasks: Record<string, AuxTaskCopy>
moaTitle: string

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These required additions need matching catalog entries. zh.ts declares a complete Translations object directly, but this PR does not change it and its head has no moaTitle, customEndpoints, or billing additions; preserve the existing interface members and update every locale catalog.

}
}

// The generic first sentence shared by the off / absent / divergent states,
// where the concrete amounts aren't the headline. The configured state overrides
// this with the disambiguating "Charges $X … below $Y." sentence (spec §8).
const AUTO_REFILL_GENERIC = 'Keep your balance topped up when it drops below your threshold.'
const AUTO_REFILL_GENERIC = translateNow('settings.billing.state.autoRefill.genericDescription')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

translateNow() reads the mutable runtime locale, so resolving this at module import permanently captures the initial language. Move this lookup into autoReloadRow() or otherwise recompute it when building the view.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform area/usage-cost Token accounting, usage reporting, billing, cost tracking labels Jul 30, 2026
@anatolijlaptev1991-ctrl
anatolijlaptev1991-ctrl force-pushed the feat/i18n-wire-hardcoded-surfaces branch from 40799b8 to d65de92 Compare July 31, 2026 08:05
@anatolijlaptev1991-ctrl

Copy link
Copy Markdown
Author

Thanks for the review — all five issues are addressed in d65de924e (rebased onto main @ f3cda0ceb).

1. Removed type fields (sayStopToEnd etc.)types.ts was rebuilt from the main type contract, then the new moa/customEndpoints/billing keys were added on top. sayStopToEnd, findInPage, artifactCard, quickEntry, wakeWord*, and every other member removed in the prior head are restored. tsc confirms the full catalog compiles.

2. zh.ts missing new keys — all new keys (moa/customEndpoints/billing/state/errors, incl. remoteSpendingReconnect, moaDisableRef/moaEnableRef) are added to zh.ts with Chinese translations. The full Translations-typed catalog now compiles.

3. AUTO_REFILL_GENERIC frozen at module init — the module-level const AUTO_REFILL_GENERIC = translateNow(...) is removed; the translation now reads the runtime locale inside the view-building path (autoReloadRow()), so it updates after a locale switch.

4. Remaining literals translated:

  • errors.ts reconnect suffix → translateNow('settings.billing.errors.remoteSpendingReconnect', who)
  • auto-reload-row.tsx 'Auto-refill updated.'translateNow('settings.billing.autoReload.updated')
  • model-settings.tsx MoA switch aria-labelt.settings.model.moaDisableRef(n) / moaEnableRef(n)

Checks (all green):

  • npm run typecheck — 3 tsconfig, 0 errors
  • eslint src/app/settings/ src/i18n/ — 0 errors (8 pre-existing warnings)
  • npm run test:ui -- i18n — 28/28 passed
  • npm run test:ui -- src/app/settings/billing — 105/107 passed; the 2 failures are pre-existing on origin/main (verified via a clean main worktree), unrelated to this PR

@anatolijlaptev1991-ctrl
anatolijlaptev1991-ctrl force-pushed the feat/i18n-wire-hardcoded-surfaces branch from d65de92 to f674592 Compare July 31, 2026 10:54
@anatolijlaptev1991-ctrl

Copy link
Copy Markdown
Author

Follow-up: 3-independent-auditor review (0c6ad7789)

After the initial sweeper feedback, ran two rounds of 3 independent auditor agents (types/contracts, literals/UX, tests/style) to scrupulously find every remaining issue. Round 1 found 5 real issues — all fixed and verified. Round 2 confirmed clean.

Round 1 findings — fixed in 0c6ad7789

ID Severity Issue Fix
B1 BLOCKER rateLimited/stripeUnavailable passed a pre-formatted English string "(try again in ~5 min)" as mins parameter — it leaked into Chinese as "请在 (try again in ~5 min) 分钟后重试" Changed mins from stringnumber (minute count). Each locale now formats its own retry suffix via ternary mins > 0 ? … : …. Signature updated in types.ts, en.ts, zh.ts.
B2 BLOCKER useSubscriptionState wasn't subscribed to locale changes — deriveBillingView could render stale translations for up to 30s (poll interval) after locale switch Added useI18n() to useSubscriptionState, matching useBillingState.
M2 MAJOR auto-reload-row.tsx — 12 hardcoded English literals (Threshold, Reload to, Turn off, Disable, Save, Saving…, Cancel, Manage, Auto-refill turned off, etc.) despite keys already existing in the catalog All 12 wired to translateNow('settings.billing.autoReload.*'). Added thresholdAria/reloadToAria keys for the two aria-labels.
M3 MAJOR plans-view.tsx — "Current plan", "Scheduled", "Downgrade" hardcoded despite bp.current/bp.scheduled/bp.downgrade existing All 3 wired to existing bp.* keys.
N1 MINOR provenanceSuffix — 3 hardcoded English provenance labels ("auto-refill card", "customer default", "subscription card") Added paymentMethod.provenance.* keys to types/en/zh; wired via 4 translateNow() calls.

Verification (all green)

  • tsc -p . --noEmit0 errors (all 3 tsconfig configs)
  • eslint src/app/settings/ src/i18n/0 errors (8 pre-existing warnings, all outside PR hunks)
  • vitest billing + i18n133/135 passed; 2 failures are pre-existing on upstream/main (verified via git checkout upstream/main + rerun — Intl.NumberFormat renders "10 $" instead of "$10" on non-en-US hosts; unrelated to i18n wiring, needs a separate billing-amounts fix)
  • CRLF — 0 contaminated files (en.ts normalized to LF in 3e570ee0e)
  • Diff scope — exactly 11 files, 0 out-of-scope

Round 2 — clean ✅

Three fresh auditors on HEAD 0c6ad7789 confirmed: all 5 round-1 fixes verified working, no new BLOCKER/MAJOR/MINOR findings. The only observation was a stylistic NIT (ASCII -> in en.ts vs Unicode in zh.ts for remoteSpendingReconnect) — both are valid, just a convention difference.

…ebased on main)

Rebased the i18n wiring onto current upstream/main after the i18n
architecture changed (locales now via defineLocale; new ar/ja/zh-hant
catalogs added). Carries the original wiring forward:

- Components (model-settings, custom-endpoints-settings, billing/*):
  replace every hard-coded English literal with t()/translateNow() calls.
- en.ts + types.ts: add the moa*/customEndpoints/billing.* key sets the
  wiring consumes (thresholdAria, reloadToAria, moaDisableRef/EnableRef,
  provenance.customerDefault/subPin/suffix, remoteSpendingReconnect, etc.).
- zh.ts: complete Chinese translations for the new key sets (strict catalog
  must implement Translations in full). ja/zh-hant/ar inherit via defineLocale.

Verification:
- tsc clean on renderer, electron, and e2e tsconfigs (0 errors)
- vitest ui: i18n 28/28 passing; billing 133/135
- The 2 billing failures are pre-existing on upstream/main (verified by
  running the same suite on a clean main checkout: 105/107) and are
  unrelated to this wiring.

Co-Authored-By: Anatoliy Laptev <anatolij.laptev.1991@gmail.com>
@anatolijlaptev1991-ctrl
anatolijlaptev1991-ctrl force-pushed the feat/i18n-wire-hardcoded-surfaces branch from 0c6ad77 to 377c49f Compare August 14, 2026 22:20
@anatolijlaptev1991-ctrl

Copy link
Copy Markdown
Author

Rebased onto current upstream/main — conflicts resolved

The branch was 2746 commits behind main and reporting CONFLICTING/DIRTY. I rebuilt it on top of the current main (the i18n architecture changed since July 30: locales are now built via defineLocale, and ar/ja/zh-hant catalogs were added).

What changed

  • Rebased the wiring onto main (now behind: 0, ahead: 1).
  • en.ts + types.ts: added the full moa* / customEndpoints / billing.* key sets the components consume (incl. thresholdAria, reloadToAria, moaDisableRef/moaEnableRef, provenance.customerDefault/subPin/suffix, remoteSpendingReconnect).
  • zh.ts: complete Chinese translations for the new key sets (it's a strict Translations catalog, so it must implement every key). ja/zh-hant/ar inherit from en via defineLocale, so they need no edits.
  • index.test.tsx: fixed an import (react-router-domreact-router).

Verification (real, on the pushed commit)

  • tsc clean on all 3 tsconfigs (renderer, electron, e2e) — 0 errors.
  • vitest ui: i18n 28/28 passing; billing 133/135.
  • The 2 billing failures (rejects auto-refill amounts…, disables buy controls while polling…) are pre-existing on upstream/main — confirmed by running the same suite on a clean main checkout (105/107). Unrelated to this wiring.

Remaining gate: an approving review (mergeStateStatus: BLOCKED is only the missing approval, not a conflict — mergeable is now MERGEABLE).

This is locale-neutral: once merged, every locale (existing or future) gets these surfaces translated automatically, complementary to any ru catalog PR (#72250, #40701).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/billing Account usage, credit usage, billing (cross-cutting) area/i18n Localization, locales, translations area/usage-cost Token accounting, usage reporting, billing, cost tracking comp/desktop Electron desktop app (apps/desktop/*) P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants