Skip to content

i18n: add Russian, and backfill every t() key into the locale bundles - #1648

Merged
FFengIll merged 2 commits into
mainfrom
claude/i18n-russian-support-nobgmh
Aug 26, 2026
Merged

FFengIll merged 2 commits into
mainfrom
claude/i18n-russian-support-nobgmh

Conversation

@andreasfoo

@andreasfoo andreasfoo commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Tingly Box shipped English and Chinese only; this adds Russian. Doing so surfaced a second problem: 326 keys the UI references were never in any locale file, so whole pages rendered English even in Chinese.

Implement #1641 .

Key Changes

  • Russian locale: full ru.ts covering every key en.ts and zh.ts define, with all four Russian plural categories on the plural keys.
  • One language source of truth: SUPPORTED_LANGUAGES now feeds the switchers, the MUI locale bundle and the heatmap's date locale, replacing scattered === 'zh' checks that mapped every other tag to English.
  • Locale coverage backfill: 326 keys existed only as inline t(…, { defaultValue }), leaving Bots / IM Notify / Remote Control largely English in every language — 130 were in no bundle at all, 196 were missing from en itself.
  • Four hardcoded strings: the Tools and Servertool nav labels, the Claude Code "Default Mode" row, and the config modal's Close button now go through t().
  • Two test guards: localeParity and tKeyCoverage fail the build when a key is missing from a bundle, instead of silently rendering English.

Notes


Generated by Claude Code

Adds Russian as a third UI language alongside English and Chinese.

- New `i18n/locales/ru.ts` covering every key in en.ts (the fallback
  bundle) plus the remoteControl / bots / notify namespaces that only
  zh.ts overrides, so Russian is not worse off than Chinese on the
  Remote Control surfaces. Plural keys carry the four Russian
  Intl.PluralRules categories (_one/_few/_many/_other).
- `i18n/index.ts` now exports SUPPORTED_LANGUAGES / AppLanguage /
  resolveLanguage as the single source of truth. The ActivityBar menu
  and badge, the System settings chips and the guide dialog toggle all
  render from that list instead of hard-coding language codes, so a
  fourth language is one row plus a locale file.
- resolveLanguage() replaces the `=== 'zh' ? 'zh' : 'en'` checks, which
  silently mapped every non-Chinese tag to English. Used by the MUI
  locale bundle (ruRU added), the token heatmap's date locale, and the
  co-located Record<AppLanguage, ...> copy in AgentNode, the Claude Code
  quick-config / config modal / profile overrides, and the Codex and dsh
  quick-config panels — all of which gained Russian entries.
- Russian nav and field labels are deliberately shorter than a literal
  translation where the ActivityBar rail (~11 chars) or the 180px
  config label column would otherwise ellipsise them; English fits those
  widths and Russian did not.
- New localeParity test asserts ru covers every key in en and in zh, so
  a future key added to en.ts cannot silently drop Russian back to
  English.

Verified: typecheck unchanged at 35 pre-existing errors (none in touched
files), lint clean, production build passes, and the test suite is back
to its 3 pre-existing failures with the new parity test green.
Screenshots of the Russian UI checked via the ui-preview skill.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018CAigqrGygAebgepPY2kjL
The codebase's inline-English pattern — t('some.key', { defaultValue: '…' })
— lets a key be referenced by the UI without existing in any locale file.
English still renders, so the gap is invisible in development, and 326 keys
had drifted out that way: the Bots, IM Notify and Remote Control pages were
largely English even in Chinese.

- 130 keys existed in no bundle at all: added to en, zh and ru.
- 196 more existed in zh/ru but not en; added to en so the fallback bundle
  is complete and the coverage guard below can be enforced.
- Two of those pass `count`, so they are registered as en plurals
  (bots.overview.subtitle, remoteControl.bots.configuredCount).

Also internationalizes four strings hardcoded in JSX: the ActivityBar's
Tools and Servertool nav labels, the Claude Code "Default Mode" row label,
and the config modal's preview Close button. ChatNode's Direct/Group chips
now use the notify.target.* keys its sibling NotifyTestDialog already used.

New tKeyCoverage test walks the sources via import.meta.glob, collects every
literal key passed to t(), and asserts all bundles define it — verified to
fail when a key is removed. Two Russian nav labels are shortened because the
ActivityBar rail clips at ~11 characters where English fits.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018CAigqrGygAebgepPY2kjL
@andreasfoo
andreasfoo force-pushed the claude/i18n-russian-support-nobgmh branch from 2dcb9a9 to dceb3f0 Compare August 26, 2026 09:46
@FFengIll
FFengIll merged commit 26980ec into main Aug 26, 2026
@FFengIll
FFengIll deleted the claude/i18n-russian-support-nobgmh branch August 26, 2026 10:11
FFengIll pushed a commit that referenced this pull request Sep 19, 2026
…#1648)

## Summary

Tingly Box shipped English and Chinese only; this adds Russian. Doing so
surfaced a second problem: 326 keys the UI references were never in any
locale file, so whole pages rendered English even in Chinese.

Implement #1641 .

## Key Changes

- **Russian locale**: full `ru.ts` covering every key `en.ts` and
`zh.ts` define, with all four Russian plural categories on the plural
keys.
- **One language source of truth**: `SUPPORTED_LANGUAGES` now feeds the
switchers, the MUI locale bundle and the heatmap's date locale,
replacing scattered `=== 'zh'` checks that mapped every other tag to
English.
- **Locale coverage backfill**: 326 keys existed only as inline `t(…, {
defaultValue })`, leaving Bots / IM Notify / Remote Control largely
English in every language — 130 were in no bundle at all, 196 were
missing from `en` itself.
- **Four hardcoded strings**: the Tools and Servertool nav labels, the
Claude Code "Default Mode" row, and the config modal's Close button now
go through `t()`.
- **Two test guards**: `localeParity` and `tKeyCoverage` fail the build
when a key is missing from a bundle, instead of silently rendering
English.

## Notes

- Two Russian nav labels use shorter synonyms than a literal
translation: the ActivityBar rail clips at ~11 characters, which English
fits and Russian did not.
- ~430 English literals remain hardcoded in JSX across 93 files;
deliberately out of scope, and `tKeyCoverage` cannot see them since they
never reach `t()`.
- Persian and Arabic, with the RTL layout work they need, are stacked on
this branch in #1646.
- Supersedes #1643, which could not be reopened after the branch was
split.

---
_Generated by [Claude
Code](https://claude.ai/code/session_018CAigqrGygAebgepPY2kjL)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

3 participants