Skip to content

[upstream #10604] feat(i18n): add Traditional Chinese (zh-TW) UI locale - #83

Open
innocarpe wants to merge 1488 commits into
mainfrom
feat/zh-hant-ui-locale
Open

innocarpe wants to merge 1488 commits into
mainfrom
feat/zh-hant-ui-locale

Conversation

@innocarpe

Copy link
Copy Markdown
Owner

Portfolio mirror of my contribution to upstream stablyai/orca.
Exhibition only — the real review/merge target is upstream.

Upstream

Summary

Summary - Adds full Traditional Chinese UI catalog (zh-TW) with Taiwan terminology (OpenCC cn→twp). - System locales zh-TW / zh-HK / zh-Hant now resolve to zh-TW instead of English. - Language picker: 中文(繁體) alongside existing 中文(简体). ## Fixes Closes stablyai#10592

Note

  • Do not merge this into innocarpe/orca main until the upstream PR is merged.
  • After upstream merges: sync fork from upstream, then close this mirror PR.
  • This open PR exists so visitors see in-flight work on this fork's Pull requests tab.

@innocarpe

Copy link
Copy Markdown
Owner Author

Sync update (d8e160f5e)

Address CodeRabbit: Taiwan terminology pass (電腦/複製/儲存/權限/帳戶), translate first-screen EN leftovers (workspace connect, remote runtime reconnect, agent dashboard). Duplicate sessionId not present in current catalog. Residual: ~600 non-core strings still match English (product names, Linear prompts, deeper settings).

nwparker and others added 28 commits August 9, 2026 18:29
…ai#9714)

* fix(ui): align toggle switch handle symmetrically in on state

The switch handle (translate-x-4 / 16px) left a 6px gap on the right
in the on state while the off state had only a 2px gap on the left.
Accounting for border-box sizing (1px border each side reduces content
width to 34px), the correct on-position is translate-x-4.5 (18px) so
both sides have a 2px inset.

* docs: add JSDoc to exported functions in changed files for docstring coverage

* fix(ui): align toggle switch handle symmetrically in on state

The `h-5 w-9` switch track is `border-box` with a 1px border, leaving 34px
of content for the 14px `size-3.5` handle. `translate-x-0.5` insets the off
state by 2px, so the on state needs 34 - 14 - 2 = 18px. It used
`translate-x-4` (16px), leaving 4px on the right against 2px on the left.

Adds a boundary test so the offset cannot drift again across the 26
hand-rolled switch call sites, and drops the docstrings the original patch
added to satisfy a coverage bot (the surrounding files carry none).

Original patch by @sei0.

Co-authored-by: Orca <help@stably.ai>

* test(ui): widen the switch-handle alignment guard to every h-5 w-9 track

Scan the renderer with the readdirSync walk and TypeScript AST already used by
no-top-level-translate.test.ts instead of shelling out to `git grep`, and find
each knob inside its own track rather than by scanning source text forward.

Why: gating on `role="switch"` silently skipped ClaudeUsageLoadingState.tsx (a
loading skeleton, so a div with no role) and HiddenExperimentalGroup.tsx, so
regressing the loading-state handle back to translate-x-4 left the guard green.

The guard now also fails when the premise behind 34 - 14 - 2 = 18px stops
holding: a track that loses its 1px border or gains padding, a conditional whose
two branches no longer pair off/on, and a track whose knob moved into a child
component where the scan would otherwise just stop seeing it.

---------

Co-authored-by: Neil <4138956+nwparker@users.noreply.github.com>
Co-authored-by: Orca <help@stably.ai>
Co-authored-by: Brennan Benson <79079362+brennanb2025@users.noreply.github.com>
* Strip liveness gate from AI Vault session delete

Delete now requires only path validation + user confirmation — no process
roster, no liveness check, no quiescence, no ownership ledger.

Co-authored-by: Orca <help@stably.ai>

* Remove obsolete AI Vault liveness delete reliability gate

Session delete no longer checks process liveness, so drop the
manifest entry that still referenced the deleted test files.

* minor fix

---------

Co-authored-by: Orca <help@stably.ai>
* Add daily macOS dev build release channel

Publish once-daily signed macOS builds from main at a dedicated cadence,
separate from hourly (too noisy) and release branches (too infrequent).
Builds are notarized and installable via the updater, but unvetted —
published to stablyai/orca-daily rather than the main repo to avoid
evicting stable/RC entries from the releases feed.

* fix lint

* fix commit

* Add third token mint to daily macOS build workflow

The upload step's 2x45m retry budget can outlive the one-hour token, so a third
is minted after it for verify and cleanup operations. Release notes are moved to
a file to ensure consistency between draft creation and publish. Daily channel
description updated with specific UTC release time.
* refactor: remove unreachable code

* refactor: simplify boolean guards
* fix: use project shell for AI Vault resumes

* fix: preserve WSL cwd fallback semantics

---------

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
* Add emoji picker to workspace editing and search

* fix(sidebar): scope sticky headers by host

* fix: address emoji picker review findings
* feat(github): add PR comment reactions

* fix(github): harden comment reaction updates

---------

Co-authored-by: Jinwoo-H <Jinwoo-H@users.noreply.github.com>
* feat: add caffeinate status controls

* refactor: compact caffeinate status

* fix: harden caffeinate readiness
Jinwoo-H and others added 27 commits August 11, 2026 20:09
* fix(runtime): preserve remote terminal pane splits

* test(runtime): tighten split authority guards

* fix(runtime): revalidate transient split sources

---------

Co-authored-by: E2E Test <e2e@test.local>
… UUID (stablyai#13881)

* fix(terminal): rank cold-park recency by activation order, not random UUID

The keep-warm exemption (stablyai#8262) broke equal hiddenSinceMs with
id.localeCompare over UUIDv4 tab ids, so which tab stays warm was a coin
flip. Ties are routine, not exotic: use-terminal-tab-cold-parking takes one
Date.now() per effect pass (stablyai#7214) and stamps every tab first seen hidden in
that pass with it, so switching away from a worktree ties all of them. The
tab that wins the flip is skipped by selectIdsBeyondHotRetain and never parks.

Ranks by an explicit activation sequence recorded on the hidden->visible edge.
Unit test covers the tie; red before, green after.

Does NOT fix terminal-hidden-view-parking.spec.ts:467, which still fails with
the same 'did not park' error on CI. Shipping this on its own merit.

Co-authored-by: Orca <help@stably.ai>

* fix(terminal): preserve focused tab across parking ties

---------

Co-authored-by: Orca <help@stably.ai>
Co-authored-by: E2E Test <e2e@test.local>
stablyai#13889) (stablyai#13925)

* fix(agent-title): treat Claude Code quarter-circle spinners as working

Claude Code 2.1.228 swapped its busy OSC title spinner from braille
(U+2800-U+28FF) to quarter circles (U+25D0/U+25D1). Orca recognized a busy
Claude title only by braille codepoints, so the new frames matched nothing.

The summary-bearing busy frame ("<glyph> Say hi in one word") carries no
"claude" name token, so it resolved to no-status. The tracker's "idle or
permission followed by no-status means the agent exited" rule then fired
mid-turn, confirmPtyAgentExit confirmed it, and the chat surface routed
exitChat -- kicking the tab to the terminal view on every message.

Widen the accepted glyph set via a shared containsAgentSpinnerGlyph helper.
Agent-specific braille frame shapes (Grok, Pi, synthetic Cursor) stay pinned
to their own glyph set.

Fixes stablyai#13889

* fix(agent-title): satisfy static analysis and trim scope
fish arms `CSI ?2031h` before painting each prompt and withdraws it when it
hands the tty to a child — a ~1ms window. Orca answered that subscribe with
`CSI ?997;Nn` across a 1-3ms renderer hop, so the reply landed after the
withdrawal and was read as stdin by the next child, corrupting `brew`/`npx`
`[y/N]` prompts.

The reply is not stale by Orca's own view when written (measured
staleReplies: 0), so no suppress-the-stale-reply scheme can close this — the
information needed to suppress does not exist yet. Nothing asked for the reply
either. The Contour spec says a terminal "should only send out the DSR when the
palette has been updated"; Ghostty (Termio.zig:729 — force=true reachable only
from the ?996n DSR), iTerm2 (VT100Terminal.m:995 — flag only) and xterm.js
(InputHandler.ts:2035 — flag only) all emit nothing on the DECSET. So stop
entering the race: record the subscription, answer nothing.

Of 17 real programs measured under a pty, only fish, tmux, claude and opencode
subscribe; none block on a reply, and answering produces one redundant palette
re-query and zero rendering difference. tmux is the only one that sends `?996n`,
which Orca still answers.

- Subscribes are record-only at all four emitters (live scan, hidden-gate fact,
  parked byte watcher, parked responder — the last is deleted, it only replied).
- `?996n` answers, the subscription registry, and the theme-flip push are
  unchanged. `paneLastThemeMode` is still seeded at subscribe so the next
  appearance re-apply is not read as a flip.
- Replay grammar carries `?2031l` alongside `?2031h`, so a late-attaching remote
  client no longer registers a subscription the TUI already retired.

Also closes fish-integration gaps found alongside: `unset` (which fish lacks)
becomes `set -e` on paths parsed by the client's login shell, `config.fish` is
parsed for agent-home detection, and bracketed-paste startup delivery is made
consistent across local/daemon/relay.

Regression test drives real fish 4.7.1 under node-pty and asserts on what the
child process reads; it fails against pre-fix code with the exact payload from
the issue. CI installs fish 4 and fails loudly rather than skipping.

Closes stablyai#9993

Co-authored-by: Orca <help@stably.ai>
* fix(terminal): carry kitty flags through Preview snapshots and pair rele

Preview was omitting the live kitty mirror from the IME bridge and dropping kitty flags from snapshots, so every commit was evaluated at flags 0. A TUI that negotiated bit-3 (report_all_keys_as_escape_codes) would receive the legacy raw text it declined.

Now the snapshot carries proven kitty flags beside their sequence boundary, the forwarder reads flags once per commit, and bit-1 (report_event_types) commits are paired with exactly one release regardless of keyup/insertText ordering. Snapshot authorities expose only the active screen's proven flags, so an old host's absent field stays unknown rather than downgraded to a manufactured zero.

* fix(terminal): sync kitty flags and IME releases across snapshots

* trim wordinesss

* fix(terminal): settle owed IME release before fresh same-key press

When a keyup is lost and the same key is pressed again, settle the stale
record's owed release instead of discarding it — this maintains correct
IME state during recovery. Also refine Kitty flag propagation to only
carry proven baselines across snapshots, and tighten related comments.

* fix(terminal): gate kitty flags on sequence boundaries

- Remote snapshots only include flags when seq is present
- Daemon uses parsed flags value when defined
- Ensures correct flag ordering in snapshot replay
…3902)

docs/design/agent-status-over-ssh.md was cited from ~10 source files but
does not exist in the repo. Replace each pointer with the invariant the
code actually relies on so the knowledge survives without the doc.

Renderer-side citations (useIpcEvents.ts, agent-status-types.ts) are left
for the concurrent batching change that owns those files.

Co-authored-by: Orca <help@stably.ai>
…tablyai#13903)

promptCacheTtlMs was a third store subscription for a field the card
already reads via foundation.settings; derive it instead.

Does NOT bundle the 26 foundation selectors as originally planned.
Measured on this store with React (150 cards, 400 unrelated writes,
26 keys): 26 separate useAppStore hooks 100.7ms; one useShallow bundle
402.5ms (4.0x worse); one bundle with a non-allocating keyed compare
141.5ms (1.4x worse). zustand v5 shallow() rebuilds two Maps from
Object.entries per compare, and a bundle must still build the whole
projection every notification, so collapsing cheap selectors only
trades away useSyncExternalStore dispatches that are cheaper than the
object it allocates. Listener count would have improved 26 -> 1 while
the hot path got slower.
* fix(agent-hooks): preserve typed Prime commands

* test(wsl): enforce Prime bridge exclusion

---------

Co-authored-by: OrcaWin <alpha-eng@stably.ai>
* fix: require authoritative worker completion verdicts

* Harden federated settlement replay

* fix(orchestration): reconcile dead retained workers

* docs: record SSH worker release coverage

* test(e2e): exercise worker settlement and release CLI

* docs: register combined orchestration CLI oracle

* test(orchestration): pin pre-ack attachment state
… title (stablyai#13957)

* fix(agents): detect a live OpenCode pane from its native OC | session title

OpenCode publishes `OC | <session>` as its OSC title, which carries no
agent-name token. detectAgentStatusFromTitle gates status on a whole-token
name match, so it returned null and every status consumer read a live
OpenCode pane as a plain shell: no "Send notes to" entry, no title-derived
sidebar row, and a title that the runtime's agent-presence check scored as
neutral. Identity already resolved (getAgentLabel returns OpenCode); only
activity was missing.

Treat the native marker as a live idle agent, placed after the spinner and
glyph checks so the decorated frames pinned by stablyai#8940 keep their status, and
accept it in the send-readiness gate the way Claude's U+2733 prefix is
accepted -- only a running OpenCode TUI ever publishes it.

* fix(agents): require spaced `OC | ` marker for native OpenCode detection

Unspaced pipes like `OC|Build` match other tools and would mistakenly
route non-OpenCode panes as send targets. Enforce literal ` | ` as
OpenCode emits it. Also clarify that only spinner decorations carry
working status, not keywords in the session summary.

* fix(agents): require OpenCode foreground process to validate native mark

OpenCode's native `OC | ` title marker now requires an active OpenCode process
to authorize agent sends, preventing false detection when the marker is left on
shell prompts. Extends wrapper prefix matching (ssh, tmux, etc.) and spinner
glyph support. Adds permission-prompt blocking signals for guarded writes.
* fix(browser): bulk clear cookies during native import

* fix(browser): separate Google cookie import warning

* fix(browser): report encrypted Google cookie exclusions

* fix(browser): skip key lookup for excluded cookies

* test(browser): cover excluded-cookie key bypass

* perf(browser): keep plaintext key scan cheap
)

Splits run-idle-cpu-benchmark.mjs into a scale fixture, an in-page timing
probe, and process sampling, and records a measured origin/main baseline so
the agent-status batching slice has an auditable before.

The agent-status write workload is not included: it needs setAgentStatuses,
so it lands with the store slice.
* Fix paired remote HTML browser preview

* Register paired HTML preview reliability gate

* Scope browser worktree selection guard

* Harden paired HTML preview ordering

* Simplify paired HTML preview ownership

* Record final paired HTML preview evidence

* fix(remote): simplify browser preview ownership

* test(remote): refresh HTML preview evidence

* fix(remote): reconcile browser create ordering

* fix(remote): guard browser focus through reconcile

* fix(remote): bound delayed browser focus

* fix(remote): guard browser focus context

* test(remote): record final HTML preview evidence

* fix(remote): keep HTML previews in background

* test(remote): cover browser focus modes

* test(remote): record focused browser contracts
Map zh-TW/zh-HK/zh-Hant system locales to a dedicated Traditional Chinese
catalog instead of falling back to English or serving Simplified Chinese.
Prefer Taiwan terms (電腦/複製/儲存/權限/帳戶) over Mainland OpenCC
residue, and translate first-screen English fallbacks for workspace
connect, remote runtime reconnect, and the agent dashboard.

Residual: ~600 catalog strings still match English (product names,
Linear skill prompts, browser cert flows, deeper settings). Full
catalog polish is out of scope for this pass.
@innocarpe
innocarpe force-pushed the feat/zh-hant-ui-locale branch from d8e160f to c7f1723 Compare August 12, 2026 16:22
@innocarpe innocarpe added the bug Something isn't working label Aug 12, 2026
@innocarpe

Copy link
Copy Markdown
Owner Author

Sync update (c7f17234c0)

Addressed the remaining Traditional Chinese localization review scope: translated the dashboard, browser certificate-error, and remote-agent detection fallbacks, and added regression coverage for the core review surfaces.\n\nValidation: 18 focused locale tests passed, full typecheck passed, Oxfmt passed, and Oxlint passed.

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Traditional Chinese (zh-Hant) UI locale