Skip to content

feat(desktop): localize conversations, sessions, and tools - #1194

Merged
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization
Jul 18, 2026
Merged

feat(desktop): localize conversations, sessions, and tools#1194
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization

Conversation

@Mingqwqqaq

Copy link
Copy Markdown
Contributor

Summary

  • move the complete fake-backend conversation journey into typed zh/en catalogs across @maka/ui and desktop presentation helpers
  • localize empty/composer states, messages, permissions, sessions, tool activity, errors, timestamps, counts, and accessibility copy through the existing reactive UI locale
  • preserve user/model/tool/path/command/generated content verbatim while preventing English catalogs from silently falling back to Chinese
  • keep session refresh/subscription callbacks on the current runtime locale and localize model/attachment/session failure paths
  • sync the stale workbar E2E contract with fix(shell): unmount the workbar toggle where no workbar exists #1186 (the unavailable toggle is unmounted, not disabled)

Boundary

This is PR 3 of #1052: conversation, session, and tool surfaces.

  • personalization.uiLocale remains the single persisted preference
  • the resolved locale remains the single React/Intl authority
  • auto -> zh remains unchanged until the PR 4 completion gate
  • remaining specialized desktop surfaces, Follow system resolution, and the final bilingual E2E matrix remain PR 4
  • user content, model output, tool-owned payloads, brands, code, commands, paths, and generated content are not translated

Verification

Linux ext4 checkout, Node 24.18.0:

  • npm run lint
  • npm run build
  • npm run typecheck
  • npx knip --workspace apps/desktop
  • npx knip --workspace packages/ui
  • npm run build:test
  • Linux sandbox smoke: 5/5
  • npm run test:dist: all workspaces passed; desktop 2701/2701
  • localized conversation/tool focused suites: 15/15
  • workbar E2E contract: passed in isolation
  • alignment audit: all fixtures clean

Local WSL/Xvfb runs throttle requestAnimationFrame to ~2 seconds in the two long-scroll geometry tests. The same two timeouts reproduce on unmodified upstream main with identical 31.6k DOM height, 19 steps, 38 frames, and ~31 second deadline; GitHub's hosted E2E job remains the authoritative gate.

Part of #1052.

@Astro-HanAstro-Han 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.

Approved. Two non-blocking localization follow-ups:

  • In the English permission prompt, the stale-request age combines localized wrapper copy with the Chinese-only formatPermissionRequestWait(), so a request older than two minutes renders text such as Waiting for 6 分钟. The duration formatter should become locale-aware.
  • Collapsed session groups still wrap counts in hard-coded full-width parentheses, producing English labels such as Archived(3). Moving the count format into the catalog would keep typography locale-correct.

Neither issue blocks this PR. The catalog ownership, reactive locale wiring, and focused coverage look good.

…ion-session-localization
# Conflicts:
#	packages/ui/src/tool-activity/agent-preview.tsx
@Mingqwqqaq
Mingqwqqaqforce-pushed the feat/1052-conversation-session-localization branch from a07353b to 90bda8bCompareJuly 18, 2026 14:53
@Astro-Han
Astro-Han merged commit b2daec5 into apache:mainJul 18, 2026
3 checks passed
GabrielDrapor added a commit to GabrielDrapor/maka-agent that referenced this pull request Aug 5, 2026
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(apache#1194's `jumpLatest`, dropped by apache#1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
Astro-Han pushed a commit that referenced this pull request Aug 5, 2026
* fix(ui): localize the Astryx chrome adopted since #1795#1795 moved the chat surfaces onto Astryx ChatLayout and deleted the
`label={copy.jumpLatest}` that fed Maka's own scroll-to-bottom button,
but never registered the replacement `@astryx.*` keys. Astryx ships no
zh catalog, so the pill has been reading "New messages" in a Chinese UI
ever since — and the same gap covers every other Astryx surface adopted
without its copy.
Audited all 219 shipped Astryx keys against the components Maka actually
renders (JSX usage, not imports — the barrel re-export makes every
component look reachable). 79 rendered keys had no override; this adds
them, reusing existing entries where the string is identical rather than
duplicating (form.clear, primitives.close/loading, selectPlaceholder).
Excluded as dead config per this file's own rule: dateInput.dialogLabel
and dateInput.placeholder — DateTimeInput references neither, and no
standalone DateInput is rendered.
The guard test listed `chat` as a surface Maka does not render, which is
what let the regression through; it now guards lightbox only, and a new
test pins the chat chrome.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): keep 跳到最新消息 for the scroll-to-bottom pill
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(#1194's `jumpLatest`, dropped by #1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(ui): assert the pinned chat overrides exist before checking them
`messages[key] ?? ''` coalesced a deleted override to the empty string,
which holds no Latin letters and satisfied the translation check on its
own — so the loop caught an English value but not a removed key.
Verified both ways: deleting the send override now fails with 'missing
override', and reverting its copy to English fails with 'untranslated'.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): localize the Lightbox reached via useLightbox
The dead-config guard's surviving entry was as stale as the one #1795
invalidated: chat-turn.tsx opens image previews through useLightbox,
which renders Astryx's Lightbox — with four English strings — while a
JSX-tag scan for <Lightbox> reports the component unrendered.
Add the four overrides (close reuses primitives.close) and retire the
ban-list guard entirely: a list keyed to "what we render today" rots
silently, and both of its entries had. Live surfaces are pinned by
presence tests instead.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(e2e): match the quote token remove button by its localized name
The token remove button read 'Remove {label}' only because Astryx's
Token had no zh override; now that @astryx.token.remove is localized the
accessible name is 移除…, and the /^Remove / locator times out. The spec
already addresses every other control by its Chinese name.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): address review — off-barrel Astryx copy, localize #642 fallback
Review findings on #2202, all verified before fixing:
- The astryx catalogue expanded SharedUiCopy, which `export *`s through
the package barrel — violating the README's off-barrel convention for
symbols with no cross-package consumer. It now lives in astryx-copy.ts,
deliberately unexported.
- The en mirror existed only as a diffable reference and was never
applied (astryxMessageOverrides returns undefined for en); dropped
rather than left to drift against upstream.
- `a` renamed to `astryx`; duplicated presence/Latin-letter assertions
extracted into assertChineseAstryxOverrides.
- P1: the #642 streaming fallback rendered a bare ChatMessage, which
resolves Astryx's shipped 'Message from {sender}' — an English
aria-label in a Chinese tree. It now reuses LocalizedChatMessage with
the same assistantAriaLabel as the TurnView path. The regression test
pins the exact trigger: wait indicators with zero turns — passing a
liveTurn instead projects a real turn and takes the localized path,
which is how the first draft of the test passed against the bug.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): prune dead overrides, sweep the whole zh map in tests
Maintainer review on #2202, each claim verified before acting:
- chatSendButton.send/stop: ChatComposerInput never renders
ChatSendButton — composer.tsx supplies its own send button. Pruned;
the pinned test now anchors on chatToolCalls.error instead.
- appShell.mobileNavigation: gated on shouldShowAutoToggle, and Maka
passes breakpoint 'none' + hasToggle false. Pruned.
- table.noData / table.filter.* / tableFiltering.filterByColumn: the
usage table mounts only the rowHeader plugin and renders EmptyState
before Table when rows are empty. Pruned.
- New whole-map sweep: every override must target a key Astryx ships,
hold no Latin outside {…}, and carry the same top-level ICU arguments
as the en default — depth-aware extraction, since plural branch text
({result}) reads like an argument to a naive regex and would flag
every zh string that drops an inapplicable plural.
- Presence-helper comment reworded: the check reads the override map
directly, so a deleted entry yields '' here; at runtime the same miss
falls back to Astryx's shipped en catalog.
Sweep red-validated both ways: a misspelled key fails membership, an
English value fails the Latin check.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto 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.

2 participants

@Mingqwqqaq@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat(desktop): localize conversations, sessions, and tools by Mingqwqqaq · Pull Request #1194 · apache/maka · GitHub
Skip to content

feat(desktop): localize conversations, sessions, and tools - #1194

Merged
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization
Jul 18, 2026
Merged

feat(desktop): localize conversations, sessions, and tools#1194
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization

Conversation

@Mingqwqqaq

Copy link
Copy Markdown
Contributor

Summary

  • move the complete fake-backend conversation journey into typed zh/en catalogs across @maka/ui and desktop presentation helpers
  • localize empty/composer states, messages, permissions, sessions, tool activity, errors, timestamps, counts, and accessibility copy through the existing reactive UI locale
  • preserve user/model/tool/path/command/generated content verbatim while preventing English catalogs from silently falling back to Chinese
  • keep session refresh/subscription callbacks on the current runtime locale and localize model/attachment/session failure paths
  • sync the stale workbar E2E contract with fix(shell): unmount the workbar toggle where no workbar exists #1186 (the unavailable toggle is unmounted, not disabled)

Boundary

This is PR 3 of #1052: conversation, session, and tool surfaces.

  • personalization.uiLocale remains the single persisted preference
  • the resolved locale remains the single React/Intl authority
  • auto -> zh remains unchanged until the PR 4 completion gate
  • remaining specialized desktop surfaces, Follow system resolution, and the final bilingual E2E matrix remain PR 4
  • user content, model output, tool-owned payloads, brands, code, commands, paths, and generated content are not translated

Verification

Linux ext4 checkout, Node 24.18.0:

  • npm run lint
  • npm run build
  • npm run typecheck
  • npx knip --workspace apps/desktop
  • npx knip --workspace packages/ui
  • npm run build:test
  • Linux sandbox smoke: 5/5
  • npm run test:dist: all workspaces passed; desktop 2701/2701
  • localized conversation/tool focused suites: 15/15
  • workbar E2E contract: passed in isolation
  • alignment audit: all fixtures clean

Local WSL/Xvfb runs throttle requestAnimationFrame to ~2 seconds in the two long-scroll geometry tests. The same two timeouts reproduce on unmodified upstream main with identical 31.6k DOM height, 19 steps, 38 frames, and ~31 second deadline; GitHub's hosted E2E job remains the authoritative gate.

Part of #1052.

@Astro-HanAstro-Han 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.

Approved. Two non-blocking localization follow-ups:

  • In the English permission prompt, the stale-request age combines localized wrapper copy with the Chinese-only formatPermissionRequestWait(), so a request older than two minutes renders text such as Waiting for 6 分钟. The duration formatter should become locale-aware.
  • Collapsed session groups still wrap counts in hard-coded full-width parentheses, producing English labels such as Archived(3). Moving the count format into the catalog would keep typography locale-correct.

Neither issue blocks this PR. The catalog ownership, reactive locale wiring, and focused coverage look good.

…ion-session-localization
# Conflicts:
#	packages/ui/src/tool-activity/agent-preview.tsx
@Mingqwqqaq
Mingqwqqaqforce-pushed the feat/1052-conversation-session-localization branch from a07353b to 90bda8bCompareJuly 18, 2026 14:53
@Astro-Han
Astro-Han merged commit b2daec5 into apache:mainJul 18, 2026
3 checks passed
GabrielDrapor added a commit to GabrielDrapor/maka-agent that referenced this pull request Aug 5, 2026
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(apache#1194's `jumpLatest`, dropped by apache#1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
Astro-Han pushed a commit that referenced this pull request Aug 5, 2026
* fix(ui): localize the Astryx chrome adopted since #1795#1795 moved the chat surfaces onto Astryx ChatLayout and deleted the
`label={copy.jumpLatest}` that fed Maka's own scroll-to-bottom button,
but never registered the replacement `@astryx.*` keys. Astryx ships no
zh catalog, so the pill has been reading "New messages" in a Chinese UI
ever since — and the same gap covers every other Astryx surface adopted
without its copy.
Audited all 219 shipped Astryx keys against the components Maka actually
renders (JSX usage, not imports — the barrel re-export makes every
component look reachable). 79 rendered keys had no override; this adds
them, reusing existing entries where the string is identical rather than
duplicating (form.clear, primitives.close/loading, selectPlaceholder).
Excluded as dead config per this file's own rule: dateInput.dialogLabel
and dateInput.placeholder — DateTimeInput references neither, and no
standalone DateInput is rendered.
The guard test listed `chat` as a surface Maka does not render, which is
what let the regression through; it now guards lightbox only, and a new
test pins the chat chrome.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): keep 跳到最新消息 for the scroll-to-bottom pill
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(#1194's `jumpLatest`, dropped by #1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(ui): assert the pinned chat overrides exist before checking them
`messages[key] ?? ''` coalesced a deleted override to the empty string,
which holds no Latin letters and satisfied the translation check on its
own — so the loop caught an English value but not a removed key.
Verified both ways: deleting the send override now fails with 'missing
override', and reverting its copy to English fails with 'untranslated'.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): localize the Lightbox reached via useLightbox
The dead-config guard's surviving entry was as stale as the one #1795
invalidated: chat-turn.tsx opens image previews through useLightbox,
which renders Astryx's Lightbox — with four English strings — while a
JSX-tag scan for <Lightbox> reports the component unrendered.
Add the four overrides (close reuses primitives.close) and retire the
ban-list guard entirely: a list keyed to "what we render today" rots
silently, and both of its entries had. Live surfaces are pinned by
presence tests instead.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(e2e): match the quote token remove button by its localized name
The token remove button read 'Remove {label}' only because Astryx's
Token had no zh override; now that @astryx.token.remove is localized the
accessible name is 移除…, and the /^Remove / locator times out. The spec
already addresses every other control by its Chinese name.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): address review — off-barrel Astryx copy, localize #642 fallback
Review findings on #2202, all verified before fixing:
- The astryx catalogue expanded SharedUiCopy, which `export *`s through
the package barrel — violating the README's off-barrel convention for
symbols with no cross-package consumer. It now lives in astryx-copy.ts,
deliberately unexported.
- The en mirror existed only as a diffable reference and was never
applied (astryxMessageOverrides returns undefined for en); dropped
rather than left to drift against upstream.
- `a` renamed to `astryx`; duplicated presence/Latin-letter assertions
extracted into assertChineseAstryxOverrides.
- P1: the #642 streaming fallback rendered a bare ChatMessage, which
resolves Astryx's shipped 'Message from {sender}' — an English
aria-label in a Chinese tree. It now reuses LocalizedChatMessage with
the same assistantAriaLabel as the TurnView path. The regression test
pins the exact trigger: wait indicators with zero turns — passing a
liveTurn instead projects a real turn and takes the localized path,
which is how the first draft of the test passed against the bug.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): prune dead overrides, sweep the whole zh map in tests
Maintainer review on #2202, each claim verified before acting:
- chatSendButton.send/stop: ChatComposerInput never renders
ChatSendButton — composer.tsx supplies its own send button. Pruned;
the pinned test now anchors on chatToolCalls.error instead.
- appShell.mobileNavigation: gated on shouldShowAutoToggle, and Maka
passes breakpoint 'none' + hasToggle false. Pruned.
- table.noData / table.filter.* / tableFiltering.filterByColumn: the
usage table mounts only the rowHeader plugin and renders EmptyState
before Table when rows are empty. Pruned.
- New whole-map sweep: every override must target a key Astryx ships,
hold no Latin outside {…}, and carry the same top-level ICU arguments
as the en default — depth-aware extraction, since plural branch text
({result}) reads like an argument to a naive regex and would flag
every zh string that drops an inapplicable plural.
- Presence-helper comment reworded: the check reads the override map
directly, so a deleted entry yields '' here; at runtime the same miss
falls back to Astryx's shipped en catalog.
Sweep red-validated both ways: a misspelled key fails membership, an
English value fails the Latin check.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto 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.

2 participants

@Mingqwqqaq@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(desktop): localize conversations, sessions, and tools by Mingqwqqaq · Pull Request #1194 · apache/maka · GitHub
Skip to content

feat(desktop): localize conversations, sessions, and tools - #1194

Merged
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization
Jul 18, 2026
Merged

feat(desktop): localize conversations, sessions, and tools#1194
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization

Conversation

@Mingqwqqaq

Copy link
Copy Markdown
Contributor

Summary

  • move the complete fake-backend conversation journey into typed zh/en catalogs across @maka/ui and desktop presentation helpers
  • localize empty/composer states, messages, permissions, sessions, tool activity, errors, timestamps, counts, and accessibility copy through the existing reactive UI locale
  • preserve user/model/tool/path/command/generated content verbatim while preventing English catalogs from silently falling back to Chinese
  • keep session refresh/subscription callbacks on the current runtime locale and localize model/attachment/session failure paths
  • sync the stale workbar E2E contract with fix(shell): unmount the workbar toggle where no workbar exists #1186 (the unavailable toggle is unmounted, not disabled)

Boundary

This is PR 3 of #1052: conversation, session, and tool surfaces.

  • personalization.uiLocale remains the single persisted preference
  • the resolved locale remains the single React/Intl authority
  • auto -> zh remains unchanged until the PR 4 completion gate
  • remaining specialized desktop surfaces, Follow system resolution, and the final bilingual E2E matrix remain PR 4
  • user content, model output, tool-owned payloads, brands, code, commands, paths, and generated content are not translated

Verification

Linux ext4 checkout, Node 24.18.0:

  • npm run lint
  • npm run build
  • npm run typecheck
  • npx knip --workspace apps/desktop
  • npx knip --workspace packages/ui
  • npm run build:test
  • Linux sandbox smoke: 5/5
  • npm run test:dist: all workspaces passed; desktop 2701/2701
  • localized conversation/tool focused suites: 15/15
  • workbar E2E contract: passed in isolation
  • alignment audit: all fixtures clean

Local WSL/Xvfb runs throttle requestAnimationFrame to ~2 seconds in the two long-scroll geometry tests. The same two timeouts reproduce on unmodified upstream main with identical 31.6k DOM height, 19 steps, 38 frames, and ~31 second deadline; GitHub's hosted E2E job remains the authoritative gate.

Part of #1052.

@Astro-HanAstro-Han 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.

Approved. Two non-blocking localization follow-ups:

  • In the English permission prompt, the stale-request age combines localized wrapper copy with the Chinese-only formatPermissionRequestWait(), so a request older than two minutes renders text such as Waiting for 6 分钟. The duration formatter should become locale-aware.
  • Collapsed session groups still wrap counts in hard-coded full-width parentheses, producing English labels such as Archived(3). Moving the count format into the catalog would keep typography locale-correct.

Neither issue blocks this PR. The catalog ownership, reactive locale wiring, and focused coverage look good.

…ion-session-localization
# Conflicts:
#	packages/ui/src/tool-activity/agent-preview.tsx
@Mingqwqqaq
Mingqwqqaqforce-pushed the feat/1052-conversation-session-localization branch from a07353b to 90bda8bCompareJuly 18, 2026 14:53
@Astro-Han
Astro-Han merged commit b2daec5 into apache:mainJul 18, 2026
3 checks passed
GabrielDrapor added a commit to GabrielDrapor/maka-agent that referenced this pull request Aug 5, 2026
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(apache#1194's `jumpLatest`, dropped by apache#1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
Astro-Han pushed a commit that referenced this pull request Aug 5, 2026
* fix(ui): localize the Astryx chrome adopted since #1795#1795 moved the chat surfaces onto Astryx ChatLayout and deleted the
`label={copy.jumpLatest}` that fed Maka's own scroll-to-bottom button,
but never registered the replacement `@astryx.*` keys. Astryx ships no
zh catalog, so the pill has been reading "New messages" in a Chinese UI
ever since — and the same gap covers every other Astryx surface adopted
without its copy.
Audited all 219 shipped Astryx keys against the components Maka actually
renders (JSX usage, not imports — the barrel re-export makes every
component look reachable). 79 rendered keys had no override; this adds
them, reusing existing entries where the string is identical rather than
duplicating (form.clear, primitives.close/loading, selectPlaceholder).
Excluded as dead config per this file's own rule: dateInput.dialogLabel
and dateInput.placeholder — DateTimeInput references neither, and no
standalone DateInput is rendered.
The guard test listed `chat` as a surface Maka does not render, which is
what let the regression through; it now guards lightbox only, and a new
test pins the chat chrome.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): keep 跳到最新消息 for the scroll-to-bottom pill
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(#1194's `jumpLatest`, dropped by #1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(ui): assert the pinned chat overrides exist before checking them
`messages[key] ?? ''` coalesced a deleted override to the empty string,
which holds no Latin letters and satisfied the translation check on its
own — so the loop caught an English value but not a removed key.
Verified both ways: deleting the send override now fails with 'missing
override', and reverting its copy to English fails with 'untranslated'.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): localize the Lightbox reached via useLightbox
The dead-config guard's surviving entry was as stale as the one #1795
invalidated: chat-turn.tsx opens image previews through useLightbox,
which renders Astryx's Lightbox — with four English strings — while a
JSX-tag scan for <Lightbox> reports the component unrendered.
Add the four overrides (close reuses primitives.close) and retire the
ban-list guard entirely: a list keyed to "what we render today" rots
silently, and both of its entries had. Live surfaces are pinned by
presence tests instead.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(e2e): match the quote token remove button by its localized name
The token remove button read 'Remove {label}' only because Astryx's
Token had no zh override; now that @astryx.token.remove is localized the
accessible name is 移除…, and the /^Remove / locator times out. The spec
already addresses every other control by its Chinese name.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): address review — off-barrel Astryx copy, localize #642 fallback
Review findings on #2202, all verified before fixing:
- The astryx catalogue expanded SharedUiCopy, which `export *`s through
the package barrel — violating the README's off-barrel convention for
symbols with no cross-package consumer. It now lives in astryx-copy.ts,
deliberately unexported.
- The en mirror existed only as a diffable reference and was never
applied (astryxMessageOverrides returns undefined for en); dropped
rather than left to drift against upstream.
- `a` renamed to `astryx`; duplicated presence/Latin-letter assertions
extracted into assertChineseAstryxOverrides.
- P1: the #642 streaming fallback rendered a bare ChatMessage, which
resolves Astryx's shipped 'Message from {sender}' — an English
aria-label in a Chinese tree. It now reuses LocalizedChatMessage with
the same assistantAriaLabel as the TurnView path. The regression test
pins the exact trigger: wait indicators with zero turns — passing a
liveTurn instead projects a real turn and takes the localized path,
which is how the first draft of the test passed against the bug.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): prune dead overrides, sweep the whole zh map in tests
Maintainer review on #2202, each claim verified before acting:
- chatSendButton.send/stop: ChatComposerInput never renders
ChatSendButton — composer.tsx supplies its own send button. Pruned;
the pinned test now anchors on chatToolCalls.error instead.
- appShell.mobileNavigation: gated on shouldShowAutoToggle, and Maka
passes breakpoint 'none' + hasToggle false. Pruned.
- table.noData / table.filter.* / tableFiltering.filterByColumn: the
usage table mounts only the rowHeader plugin and renders EmptyState
before Table when rows are empty. Pruned.
- New whole-map sweep: every override must target a key Astryx ships,
hold no Latin outside {…}, and carry the same top-level ICU arguments
as the en default — depth-aware extraction, since plural branch text
({result}) reads like an argument to a naive regex and would flag
every zh string that drops an inapplicable plural.
- Presence-helper comment reworded: the check reads the override map
directly, so a deleted entry yields '' here; at runtime the same miss
falls back to Astryx's shipped en catalog.
Sweep red-validated both ways: a misspelled key fails membership, an
English value fails the Latin check.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto 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.

2 participants

@Mingqwqqaq@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(desktop): localize conversations, sessions, and tools by Mingqwqqaq · Pull Request #1194 · apache/maka · GitHub
Skip to content

feat(desktop): localize conversations, sessions, and tools - #1194

Merged
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization
Jul 18, 2026
Merged

feat(desktop): localize conversations, sessions, and tools#1194
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization

Conversation

@Mingqwqqaq

Copy link
Copy Markdown
Contributor

Summary

  • move the complete fake-backend conversation journey into typed zh/en catalogs across @maka/ui and desktop presentation helpers
  • localize empty/composer states, messages, permissions, sessions, tool activity, errors, timestamps, counts, and accessibility copy through the existing reactive UI locale
  • preserve user/model/tool/path/command/generated content verbatim while preventing English catalogs from silently falling back to Chinese
  • keep session refresh/subscription callbacks on the current runtime locale and localize model/attachment/session failure paths
  • sync the stale workbar E2E contract with fix(shell): unmount the workbar toggle where no workbar exists #1186 (the unavailable toggle is unmounted, not disabled)

Boundary

This is PR 3 of #1052: conversation, session, and tool surfaces.

  • personalization.uiLocale remains the single persisted preference
  • the resolved locale remains the single React/Intl authority
  • auto -> zh remains unchanged until the PR 4 completion gate
  • remaining specialized desktop surfaces, Follow system resolution, and the final bilingual E2E matrix remain PR 4
  • user content, model output, tool-owned payloads, brands, code, commands, paths, and generated content are not translated

Verification

Linux ext4 checkout, Node 24.18.0:

  • npm run lint
  • npm run build
  • npm run typecheck
  • npx knip --workspace apps/desktop
  • npx knip --workspace packages/ui
  • npm run build:test
  • Linux sandbox smoke: 5/5
  • npm run test:dist: all workspaces passed; desktop 2701/2701
  • localized conversation/tool focused suites: 15/15
  • workbar E2E contract: passed in isolation
  • alignment audit: all fixtures clean

Local WSL/Xvfb runs throttle requestAnimationFrame to ~2 seconds in the two long-scroll geometry tests. The same two timeouts reproduce on unmodified upstream main with identical 31.6k DOM height, 19 steps, 38 frames, and ~31 second deadline; GitHub's hosted E2E job remains the authoritative gate.

Part of #1052.

@Astro-HanAstro-Han 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.

Approved. Two non-blocking localization follow-ups:

  • In the English permission prompt, the stale-request age combines localized wrapper copy with the Chinese-only formatPermissionRequestWait(), so a request older than two minutes renders text such as Waiting for 6 分钟. The duration formatter should become locale-aware.
  • Collapsed session groups still wrap counts in hard-coded full-width parentheses, producing English labels such as Archived(3). Moving the count format into the catalog would keep typography locale-correct.

Neither issue blocks this PR. The catalog ownership, reactive locale wiring, and focused coverage look good.

…ion-session-localization
# Conflicts:
#	packages/ui/src/tool-activity/agent-preview.tsx
@Mingqwqqaq
Mingqwqqaqforce-pushed the feat/1052-conversation-session-localization branch from a07353b to 90bda8bCompareJuly 18, 2026 14:53
@Astro-Han
Astro-Han merged commit b2daec5 into apache:mainJul 18, 2026
3 checks passed
GabrielDrapor added a commit to GabrielDrapor/maka-agent that referenced this pull request Aug 5, 2026
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(apache#1194's `jumpLatest`, dropped by apache#1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
Astro-Han pushed a commit that referenced this pull request Aug 5, 2026
* fix(ui): localize the Astryx chrome adopted since #1795#1795 moved the chat surfaces onto Astryx ChatLayout and deleted the
`label={copy.jumpLatest}` that fed Maka's own scroll-to-bottom button,
but never registered the replacement `@astryx.*` keys. Astryx ships no
zh catalog, so the pill has been reading "New messages" in a Chinese UI
ever since — and the same gap covers every other Astryx surface adopted
without its copy.
Audited all 219 shipped Astryx keys against the components Maka actually
renders (JSX usage, not imports — the barrel re-export makes every
component look reachable). 79 rendered keys had no override; this adds
them, reusing existing entries where the string is identical rather than
duplicating (form.clear, primitives.close/loading, selectPlaceholder).
Excluded as dead config per this file's own rule: dateInput.dialogLabel
and dateInput.placeholder — DateTimeInput references neither, and no
standalone DateInput is rendered.
The guard test listed `chat` as a surface Maka does not render, which is
what let the regression through; it now guards lightbox only, and a new
test pins the chat chrome.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): keep 跳到最新消息 for the scroll-to-bottom pill
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(#1194's `jumpLatest`, dropped by #1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(ui): assert the pinned chat overrides exist before checking them
`messages[key] ?? ''` coalesced a deleted override to the empty string,
which holds no Latin letters and satisfied the translation check on its
own — so the loop caught an English value but not a removed key.
Verified both ways: deleting the send override now fails with 'missing
override', and reverting its copy to English fails with 'untranslated'.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): localize the Lightbox reached via useLightbox
The dead-config guard's surviving entry was as stale as the one #1795
invalidated: chat-turn.tsx opens image previews through useLightbox,
which renders Astryx's Lightbox — with four English strings — while a
JSX-tag scan for <Lightbox> reports the component unrendered.
Add the four overrides (close reuses primitives.close) and retire the
ban-list guard entirely: a list keyed to "what we render today" rots
silently, and both of its entries had. Live surfaces are pinned by
presence tests instead.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(e2e): match the quote token remove button by its localized name
The token remove button read 'Remove {label}' only because Astryx's
Token had no zh override; now that @astryx.token.remove is localized the
accessible name is 移除…, and the /^Remove / locator times out. The spec
already addresses every other control by its Chinese name.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): address review — off-barrel Astryx copy, localize #642 fallback
Review findings on #2202, all verified before fixing:
- The astryx catalogue expanded SharedUiCopy, which `export *`s through
the package barrel — violating the README's off-barrel convention for
symbols with no cross-package consumer. It now lives in astryx-copy.ts,
deliberately unexported.
- The en mirror existed only as a diffable reference and was never
applied (astryxMessageOverrides returns undefined for en); dropped
rather than left to drift against upstream.
- `a` renamed to `astryx`; duplicated presence/Latin-letter assertions
extracted into assertChineseAstryxOverrides.
- P1: the #642 streaming fallback rendered a bare ChatMessage, which
resolves Astryx's shipped 'Message from {sender}' — an English
aria-label in a Chinese tree. It now reuses LocalizedChatMessage with
the same assistantAriaLabel as the TurnView path. The regression test
pins the exact trigger: wait indicators with zero turns — passing a
liveTurn instead projects a real turn and takes the localized path,
which is how the first draft of the test passed against the bug.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): prune dead overrides, sweep the whole zh map in tests
Maintainer review on #2202, each claim verified before acting:
- chatSendButton.send/stop: ChatComposerInput never renders
ChatSendButton — composer.tsx supplies its own send button. Pruned;
the pinned test now anchors on chatToolCalls.error instead.
- appShell.mobileNavigation: gated on shouldShowAutoToggle, and Maka
passes breakpoint 'none' + hasToggle false. Pruned.
- table.noData / table.filter.* / tableFiltering.filterByColumn: the
usage table mounts only the rowHeader plugin and renders EmptyState
before Table when rows are empty. Pruned.
- New whole-map sweep: every override must target a key Astryx ships,
hold no Latin outside {…}, and carry the same top-level ICU arguments
as the en default — depth-aware extraction, since plural branch text
({result}) reads like an argument to a naive regex and would flag
every zh string that drops an inapplicable plural.
- Presence-helper comment reworded: the check reads the override map
directly, so a deleted entry yields '' here; at runtime the same miss
falls back to Astryx's shipped en catalog.
Sweep red-validated both ways: a misspelled key fails membership, an
English value fails the Latin check.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto 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.

2 participants

@Mingqwqqaq@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat(desktop): localize conversations, sessions, and tools by Mingqwqqaq · Pull Request #1194 · apache/maka · GitHub
Skip to content

feat(desktop): localize conversations, sessions, and tools - #1194

Merged
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization
Jul 18, 2026
Merged

feat(desktop): localize conversations, sessions, and tools#1194
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization

Conversation

@Mingqwqqaq

Copy link
Copy Markdown
Contributor

Summary

  • move the complete fake-backend conversation journey into typed zh/en catalogs across @maka/ui and desktop presentation helpers
  • localize empty/composer states, messages, permissions, sessions, tool activity, errors, timestamps, counts, and accessibility copy through the existing reactive UI locale
  • preserve user/model/tool/path/command/generated content verbatim while preventing English catalogs from silently falling back to Chinese
  • keep session refresh/subscription callbacks on the current runtime locale and localize model/attachment/session failure paths
  • sync the stale workbar E2E contract with fix(shell): unmount the workbar toggle where no workbar exists #1186 (the unavailable toggle is unmounted, not disabled)

Boundary

This is PR 3 of #1052: conversation, session, and tool surfaces.

  • personalization.uiLocale remains the single persisted preference
  • the resolved locale remains the single React/Intl authority
  • auto -> zh remains unchanged until the PR 4 completion gate
  • remaining specialized desktop surfaces, Follow system resolution, and the final bilingual E2E matrix remain PR 4
  • user content, model output, tool-owned payloads, brands, code, commands, paths, and generated content are not translated

Verification

Linux ext4 checkout, Node 24.18.0:

  • npm run lint
  • npm run build
  • npm run typecheck
  • npx knip --workspace apps/desktop
  • npx knip --workspace packages/ui
  • npm run build:test
  • Linux sandbox smoke: 5/5
  • npm run test:dist: all workspaces passed; desktop 2701/2701
  • localized conversation/tool focused suites: 15/15
  • workbar E2E contract: passed in isolation
  • alignment audit: all fixtures clean

Local WSL/Xvfb runs throttle requestAnimationFrame to ~2 seconds in the two long-scroll geometry tests. The same two timeouts reproduce on unmodified upstream main with identical 31.6k DOM height, 19 steps, 38 frames, and ~31 second deadline; GitHub's hosted E2E job remains the authoritative gate.

Part of #1052.

@Astro-HanAstro-Han 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.

Approved. Two non-blocking localization follow-ups:

  • In the English permission prompt, the stale-request age combines localized wrapper copy with the Chinese-only formatPermissionRequestWait(), so a request older than two minutes renders text such as Waiting for 6 分钟. The duration formatter should become locale-aware.
  • Collapsed session groups still wrap counts in hard-coded full-width parentheses, producing English labels such as Archived(3). Moving the count format into the catalog would keep typography locale-correct.

Neither issue blocks this PR. The catalog ownership, reactive locale wiring, and focused coverage look good.

…ion-session-localization
# Conflicts:
#	packages/ui/src/tool-activity/agent-preview.tsx
@Mingqwqqaq
Mingqwqqaqforce-pushed the feat/1052-conversation-session-localization branch from a07353b to 90bda8bCompareJuly 18, 2026 14:53
@Astro-Han
Astro-Han merged commit b2daec5 into apache:mainJul 18, 2026
3 checks passed
GabrielDrapor added a commit to GabrielDrapor/maka-agent that referenced this pull request Aug 5, 2026
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(apache#1194's `jumpLatest`, dropped by apache#1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
Astro-Han pushed a commit that referenced this pull request Aug 5, 2026
* fix(ui): localize the Astryx chrome adopted since #1795#1795 moved the chat surfaces onto Astryx ChatLayout and deleted the
`label={copy.jumpLatest}` that fed Maka's own scroll-to-bottom button,
but never registered the replacement `@astryx.*` keys. Astryx ships no
zh catalog, so the pill has been reading "New messages" in a Chinese UI
ever since — and the same gap covers every other Astryx surface adopted
without its copy.
Audited all 219 shipped Astryx keys against the components Maka actually
renders (JSX usage, not imports — the barrel re-export makes every
component look reachable). 79 rendered keys had no override; this adds
them, reusing existing entries where the string is identical rather than
duplicating (form.clear, primitives.close/loading, selectPlaceholder).
Excluded as dead config per this file's own rule: dateInput.dialogLabel
and dateInput.placeholder — DateTimeInput references neither, and no
standalone DateInput is rendered.
The guard test listed `chat` as a surface Maka does not render, which is
what let the regression through; it now guards lightbox only, and a new
test pins the chat chrome.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): keep 跳到最新消息 for the scroll-to-bottom pill
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(#1194's `jumpLatest`, dropped by #1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(ui): assert the pinned chat overrides exist before checking them
`messages[key] ?? ''` coalesced a deleted override to the empty string,
which holds no Latin letters and satisfied the translation check on its
own — so the loop caught an English value but not a removed key.
Verified both ways: deleting the send override now fails with 'missing
override', and reverting its copy to English fails with 'untranslated'.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): localize the Lightbox reached via useLightbox
The dead-config guard's surviving entry was as stale as the one #1795
invalidated: chat-turn.tsx opens image previews through useLightbox,
which renders Astryx's Lightbox — with four English strings — while a
JSX-tag scan for <Lightbox> reports the component unrendered.
Add the four overrides (close reuses primitives.close) and retire the
ban-list guard entirely: a list keyed to "what we render today" rots
silently, and both of its entries had. Live surfaces are pinned by
presence tests instead.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(e2e): match the quote token remove button by its localized name
The token remove button read 'Remove {label}' only because Astryx's
Token had no zh override; now that @astryx.token.remove is localized the
accessible name is 移除…, and the /^Remove / locator times out. The spec
already addresses every other control by its Chinese name.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): address review — off-barrel Astryx copy, localize #642 fallback
Review findings on #2202, all verified before fixing:
- The astryx catalogue expanded SharedUiCopy, which `export *`s through
the package barrel — violating the README's off-barrel convention for
symbols with no cross-package consumer. It now lives in astryx-copy.ts,
deliberately unexported.
- The en mirror existed only as a diffable reference and was never
applied (astryxMessageOverrides returns undefined for en); dropped
rather than left to drift against upstream.
- `a` renamed to `astryx`; duplicated presence/Latin-letter assertions
extracted into assertChineseAstryxOverrides.
- P1: the #642 streaming fallback rendered a bare ChatMessage, which
resolves Astryx's shipped 'Message from {sender}' — an English
aria-label in a Chinese tree. It now reuses LocalizedChatMessage with
the same assistantAriaLabel as the TurnView path. The regression test
pins the exact trigger: wait indicators with zero turns — passing a
liveTurn instead projects a real turn and takes the localized path,
which is how the first draft of the test passed against the bug.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): prune dead overrides, sweep the whole zh map in tests
Maintainer review on #2202, each claim verified before acting:
- chatSendButton.send/stop: ChatComposerInput never renders
ChatSendButton — composer.tsx supplies its own send button. Pruned;
the pinned test now anchors on chatToolCalls.error instead.
- appShell.mobileNavigation: gated on shouldShowAutoToggle, and Maka
passes breakpoint 'none' + hasToggle false. Pruned.
- table.noData / table.filter.* / tableFiltering.filterByColumn: the
usage table mounts only the rowHeader plugin and renders EmptyState
before Table when rows are empty. Pruned.
- New whole-map sweep: every override must target a key Astryx ships,
hold no Latin outside {…}, and carry the same top-level ICU arguments
as the en default — depth-aware extraction, since plural branch text
({result}) reads like an argument to a naive regex and would flag
every zh string that drops an inapplicable plural.
- Presence-helper comment reworded: the check reads the override map
directly, so a deleted entry yields '' here; at runtime the same miss
falls back to Astryx's shipped en catalog.
Sweep red-validated both ways: a misspelled key fails membership, an
English value fails the Latin check.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto 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.

2 participants

@Mingqwqqaq@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(desktop): localize conversations, sessions, and tools by Mingqwqqaq · Pull Request #1194 · apache/maka · GitHub
Skip to content

feat(desktop): localize conversations, sessions, and tools - #1194

Merged
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization
Jul 18, 2026
Merged

feat(desktop): localize conversations, sessions, and tools#1194
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization

Conversation

@Mingqwqqaq

Copy link
Copy Markdown
Contributor

Summary

  • move the complete fake-backend conversation journey into typed zh/en catalogs across @maka/ui and desktop presentation helpers
  • localize empty/composer states, messages, permissions, sessions, tool activity, errors, timestamps, counts, and accessibility copy through the existing reactive UI locale
  • preserve user/model/tool/path/command/generated content verbatim while preventing English catalogs from silently falling back to Chinese
  • keep session refresh/subscription callbacks on the current runtime locale and localize model/attachment/session failure paths
  • sync the stale workbar E2E contract with fix(shell): unmount the workbar toggle where no workbar exists #1186 (the unavailable toggle is unmounted, not disabled)

Boundary

This is PR 3 of #1052: conversation, session, and tool surfaces.

  • personalization.uiLocale remains the single persisted preference
  • the resolved locale remains the single React/Intl authority
  • auto -> zh remains unchanged until the PR 4 completion gate
  • remaining specialized desktop surfaces, Follow system resolution, and the final bilingual E2E matrix remain PR 4
  • user content, model output, tool-owned payloads, brands, code, commands, paths, and generated content are not translated

Verification

Linux ext4 checkout, Node 24.18.0:

  • npm run lint
  • npm run build
  • npm run typecheck
  • npx knip --workspace apps/desktop
  • npx knip --workspace packages/ui
  • npm run build:test
  • Linux sandbox smoke: 5/5
  • npm run test:dist: all workspaces passed; desktop 2701/2701
  • localized conversation/tool focused suites: 15/15
  • workbar E2E contract: passed in isolation
  • alignment audit: all fixtures clean

Local WSL/Xvfb runs throttle requestAnimationFrame to ~2 seconds in the two long-scroll geometry tests. The same two timeouts reproduce on unmodified upstream main with identical 31.6k DOM height, 19 steps, 38 frames, and ~31 second deadline; GitHub's hosted E2E job remains the authoritative gate.

Part of #1052.

@Astro-HanAstro-Han 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.

Approved. Two non-blocking localization follow-ups:

  • In the English permission prompt, the stale-request age combines localized wrapper copy with the Chinese-only formatPermissionRequestWait(), so a request older than two minutes renders text such as Waiting for 6 分钟. The duration formatter should become locale-aware.
  • Collapsed session groups still wrap counts in hard-coded full-width parentheses, producing English labels such as Archived(3). Moving the count format into the catalog would keep typography locale-correct.

Neither issue blocks this PR. The catalog ownership, reactive locale wiring, and focused coverage look good.

…ion-session-localization
# Conflicts:
#	packages/ui/src/tool-activity/agent-preview.tsx
@Mingqwqqaq
Mingqwqqaqforce-pushed the feat/1052-conversation-session-localization branch from a07353b to 90bda8bCompareJuly 18, 2026 14:53
@Astro-Han
Astro-Han merged commit b2daec5 into apache:mainJul 18, 2026
3 checks passed
GabrielDrapor added a commit to GabrielDrapor/maka-agent that referenced this pull request Aug 5, 2026
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(apache#1194's `jumpLatest`, dropped by apache#1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
Astro-Han pushed a commit that referenced this pull request Aug 5, 2026
* fix(ui): localize the Astryx chrome adopted since #1795#1795 moved the chat surfaces onto Astryx ChatLayout and deleted the
`label={copy.jumpLatest}` that fed Maka's own scroll-to-bottom button,
but never registered the replacement `@astryx.*` keys. Astryx ships no
zh catalog, so the pill has been reading "New messages" in a Chinese UI
ever since — and the same gap covers every other Astryx surface adopted
without its copy.
Audited all 219 shipped Astryx keys against the components Maka actually
renders (JSX usage, not imports — the barrel re-export makes every
component look reachable). 79 rendered keys had no override; this adds
them, reusing existing entries where the string is identical rather than
duplicating (form.clear, primitives.close/loading, selectPlaceholder).
Excluded as dead config per this file's own rule: dateInput.dialogLabel
and dateInput.placeholder — DateTimeInput references neither, and no
standalone DateInput is rendered.
The guard test listed `chat` as a surface Maka does not render, which is
what let the regression through; it now guards lightbox only, and a new
test pins the chat chrome.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): keep 跳到最新消息 for the scroll-to-bottom pill
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(#1194's `jumpLatest`, dropped by #1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(ui): assert the pinned chat overrides exist before checking them
`messages[key] ?? ''` coalesced a deleted override to the empty string,
which holds no Latin letters and satisfied the translation check on its
own — so the loop caught an English value but not a removed key.
Verified both ways: deleting the send override now fails with 'missing
override', and reverting its copy to English fails with 'untranslated'.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): localize the Lightbox reached via useLightbox
The dead-config guard's surviving entry was as stale as the one #1795
invalidated: chat-turn.tsx opens image previews through useLightbox,
which renders Astryx's Lightbox — with four English strings — while a
JSX-tag scan for <Lightbox> reports the component unrendered.
Add the four overrides (close reuses primitives.close) and retire the
ban-list guard entirely: a list keyed to "what we render today" rots
silently, and both of its entries had. Live surfaces are pinned by
presence tests instead.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(e2e): match the quote token remove button by its localized name
The token remove button read 'Remove {label}' only because Astryx's
Token had no zh override; now that @astryx.token.remove is localized the
accessible name is 移除…, and the /^Remove / locator times out. The spec
already addresses every other control by its Chinese name.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): address review — off-barrel Astryx copy, localize #642 fallback
Review findings on #2202, all verified before fixing:
- The astryx catalogue expanded SharedUiCopy, which `export *`s through
the package barrel — violating the README's off-barrel convention for
symbols with no cross-package consumer. It now lives in astryx-copy.ts,
deliberately unexported.
- The en mirror existed only as a diffable reference and was never
applied (astryxMessageOverrides returns undefined for en); dropped
rather than left to drift against upstream.
- `a` renamed to `astryx`; duplicated presence/Latin-letter assertions
extracted into assertChineseAstryxOverrides.
- P1: the #642 streaming fallback rendered a bare ChatMessage, which
resolves Astryx's shipped 'Message from {sender}' — an English
aria-label in a Chinese tree. It now reuses LocalizedChatMessage with
the same assistantAriaLabel as the TurnView path. The regression test
pins the exact trigger: wait indicators with zero turns — passing a
liveTurn instead projects a real turn and takes the localized path,
which is how the first draft of the test passed against the bug.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): prune dead overrides, sweep the whole zh map in tests
Maintainer review on #2202, each claim verified before acting:
- chatSendButton.send/stop: ChatComposerInput never renders
ChatSendButton — composer.tsx supplies its own send button. Pruned;
the pinned test now anchors on chatToolCalls.error instead.
- appShell.mobileNavigation: gated on shouldShowAutoToggle, and Maka
passes breakpoint 'none' + hasToggle false. Pruned.
- table.noData / table.filter.* / tableFiltering.filterByColumn: the
usage table mounts only the rowHeader plugin and renders EmptyState
before Table when rows are empty. Pruned.
- New whole-map sweep: every override must target a key Astryx ships,
hold no Latin outside {…}, and carry the same top-level ICU arguments
as the en default — depth-aware extraction, since plural branch text
({result}) reads like an argument to a naive regex and would flag
every zh string that drops an inapplicable plural.
- Presence-helper comment reworded: the check reads the override map
directly, so a deleted entry yields '' here; at runtime the same miss
falls back to Astryx's shipped en catalog.
Sweep red-validated both ways: a misspelled key fails membership, an
English value fails the Latin check.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto 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.

2 participants

@Mingqwqqaq@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(desktop): localize conversations, sessions, and tools by Mingqwqqaq · Pull Request #1194 · apache/maka · GitHub
Skip to content

feat(desktop): localize conversations, sessions, and tools - #1194

Merged
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization
Jul 18, 2026
Merged

feat(desktop): localize conversations, sessions, and tools#1194
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization

Conversation

@Mingqwqqaq

Copy link
Copy Markdown
Contributor

Summary

  • move the complete fake-backend conversation journey into typed zh/en catalogs across @maka/ui and desktop presentation helpers
  • localize empty/composer states, messages, permissions, sessions, tool activity, errors, timestamps, counts, and accessibility copy through the existing reactive UI locale
  • preserve user/model/tool/path/command/generated content verbatim while preventing English catalogs from silently falling back to Chinese
  • keep session refresh/subscription callbacks on the current runtime locale and localize model/attachment/session failure paths
  • sync the stale workbar E2E contract with fix(shell): unmount the workbar toggle where no workbar exists #1186 (the unavailable toggle is unmounted, not disabled)

Boundary

This is PR 3 of #1052: conversation, session, and tool surfaces.

  • personalization.uiLocale remains the single persisted preference
  • the resolved locale remains the single React/Intl authority
  • auto -> zh remains unchanged until the PR 4 completion gate
  • remaining specialized desktop surfaces, Follow system resolution, and the final bilingual E2E matrix remain PR 4
  • user content, model output, tool-owned payloads, brands, code, commands, paths, and generated content are not translated

Verification

Linux ext4 checkout, Node 24.18.0:

  • npm run lint
  • npm run build
  • npm run typecheck
  • npx knip --workspace apps/desktop
  • npx knip --workspace packages/ui
  • npm run build:test
  • Linux sandbox smoke: 5/5
  • npm run test:dist: all workspaces passed; desktop 2701/2701
  • localized conversation/tool focused suites: 15/15
  • workbar E2E contract: passed in isolation
  • alignment audit: all fixtures clean

Local WSL/Xvfb runs throttle requestAnimationFrame to ~2 seconds in the two long-scroll geometry tests. The same two timeouts reproduce on unmodified upstream main with identical 31.6k DOM height, 19 steps, 38 frames, and ~31 second deadline; GitHub's hosted E2E job remains the authoritative gate.

Part of #1052.

@Astro-HanAstro-Han 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.

Approved. Two non-blocking localization follow-ups:

  • In the English permission prompt, the stale-request age combines localized wrapper copy with the Chinese-only formatPermissionRequestWait(), so a request older than two minutes renders text such as Waiting for 6 分钟. The duration formatter should become locale-aware.
  • Collapsed session groups still wrap counts in hard-coded full-width parentheses, producing English labels such as Archived(3). Moving the count format into the catalog would keep typography locale-correct.

Neither issue blocks this PR. The catalog ownership, reactive locale wiring, and focused coverage look good.

…ion-session-localization
# Conflicts:
#	packages/ui/src/tool-activity/agent-preview.tsx
@Mingqwqqaq
Mingqwqqaqforce-pushed the feat/1052-conversation-session-localization branch from a07353b to 90bda8bCompareJuly 18, 2026 14:53
@Astro-Han
Astro-Han merged commit b2daec5 into apache:mainJul 18, 2026
3 checks passed
GabrielDrapor added a commit to GabrielDrapor/maka-agent that referenced this pull request Aug 5, 2026
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(apache#1194's `jumpLatest`, dropped by apache#1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
Astro-Han pushed a commit that referenced this pull request Aug 5, 2026
* fix(ui): localize the Astryx chrome adopted since #1795#1795 moved the chat surfaces onto Astryx ChatLayout and deleted the
`label={copy.jumpLatest}` that fed Maka's own scroll-to-bottom button,
but never registered the replacement `@astryx.*` keys. Astryx ships no
zh catalog, so the pill has been reading "New messages" in a Chinese UI
ever since — and the same gap covers every other Astryx surface adopted
without its copy.
Audited all 219 shipped Astryx keys against the components Maka actually
renders (JSX usage, not imports — the barrel re-export makes every
component look reachable). 79 rendered keys had no override; this adds
them, reusing existing entries where the string is identical rather than
duplicating (form.clear, primitives.close/loading, selectPlaceholder).
Excluded as dead config per this file's own rule: dateInput.dialogLabel
and dateInput.placeholder — DateTimeInput references neither, and no
standalone DateInput is rendered.
The guard test listed `chat` as a surface Maka does not render, which is
what let the regression through; it now guards lightbox only, and a new
test pins the chat chrome.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): keep 跳到最新消息 for the scroll-to-bottom pill
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(#1194's `jumpLatest`, dropped by #1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(ui): assert the pinned chat overrides exist before checking them
`messages[key] ?? ''` coalesced a deleted override to the empty string,
which holds no Latin letters and satisfied the translation check on its
own — so the loop caught an English value but not a removed key.
Verified both ways: deleting the send override now fails with 'missing
override', and reverting its copy to English fails with 'untranslated'.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): localize the Lightbox reached via useLightbox
The dead-config guard's surviving entry was as stale as the one #1795
invalidated: chat-turn.tsx opens image previews through useLightbox,
which renders Astryx's Lightbox — with four English strings — while a
JSX-tag scan for <Lightbox> reports the component unrendered.
Add the four overrides (close reuses primitives.close) and retire the
ban-list guard entirely: a list keyed to "what we render today" rots
silently, and both of its entries had. Live surfaces are pinned by
presence tests instead.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(e2e): match the quote token remove button by its localized name
The token remove button read 'Remove {label}' only because Astryx's
Token had no zh override; now that @astryx.token.remove is localized the
accessible name is 移除…, and the /^Remove / locator times out. The spec
already addresses every other control by its Chinese name.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): address review — off-barrel Astryx copy, localize #642 fallback
Review findings on #2202, all verified before fixing:
- The astryx catalogue expanded SharedUiCopy, which `export *`s through
the package barrel — violating the README's off-barrel convention for
symbols with no cross-package consumer. It now lives in astryx-copy.ts,
deliberately unexported.
- The en mirror existed only as a diffable reference and was never
applied (astryxMessageOverrides returns undefined for en); dropped
rather than left to drift against upstream.
- `a` renamed to `astryx`; duplicated presence/Latin-letter assertions
extracted into assertChineseAstryxOverrides.
- P1: the #642 streaming fallback rendered a bare ChatMessage, which
resolves Astryx's shipped 'Message from {sender}' — an English
aria-label in a Chinese tree. It now reuses LocalizedChatMessage with
the same assistantAriaLabel as the TurnView path. The regression test
pins the exact trigger: wait indicators with zero turns — passing a
liveTurn instead projects a real turn and takes the localized path,
which is how the first draft of the test passed against the bug.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): prune dead overrides, sweep the whole zh map in tests
Maintainer review on #2202, each claim verified before acting:
- chatSendButton.send/stop: ChatComposerInput never renders
ChatSendButton — composer.tsx supplies its own send button. Pruned;
the pinned test now anchors on chatToolCalls.error instead.
- appShell.mobileNavigation: gated on shouldShowAutoToggle, and Maka
passes breakpoint 'none' + hasToggle false. Pruned.
- table.noData / table.filter.* / tableFiltering.filterByColumn: the
usage table mounts only the rowHeader plugin and renders EmptyState
before Table when rows are empty. Pruned.
- New whole-map sweep: every override must target a key Astryx ships,
hold no Latin outside {…}, and carry the same top-level ICU arguments
as the en default — depth-aware extraction, since plural branch text
({result}) reads like an argument to a naive regex and would flag
every zh string that drops an inapplicable plural.
- Presence-helper comment reworded: the check reads the override map
directly, so a deleted entry yields '' here; at runtime the same miss
falls back to Astryx's shipped en catalog.
Sweep red-validated both ways: a misspelled key fails membership, an
English value fails the Latin check.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto 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.

2 participants

@Mingqwqqaq@Astro-Han
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat(desktop): localize conversations, sessions, and tools by Mingqwqqaq · Pull Request #1194 · apache/maka · GitHub
Skip to content

feat(desktop): localize conversations, sessions, and tools - #1194

Merged
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization
Jul 18, 2026
Merged

feat(desktop): localize conversations, sessions, and tools#1194
Astro-Han merged 11 commits into
apache:mainfrom
Mingqwqqaq:feat/1052-conversation-session-localization

Conversation

@Mingqwqqaq

Copy link
Copy Markdown
Contributor

Summary

  • move the complete fake-backend conversation journey into typed zh/en catalogs across @maka/ui and desktop presentation helpers
  • localize empty/composer states, messages, permissions, sessions, tool activity, errors, timestamps, counts, and accessibility copy through the existing reactive UI locale
  • preserve user/model/tool/path/command/generated content verbatim while preventing English catalogs from silently falling back to Chinese
  • keep session refresh/subscription callbacks on the current runtime locale and localize model/attachment/session failure paths
  • sync the stale workbar E2E contract with fix(shell): unmount the workbar toggle where no workbar exists #1186 (the unavailable toggle is unmounted, not disabled)

Boundary

This is PR 3 of #1052: conversation, session, and tool surfaces.

  • personalization.uiLocale remains the single persisted preference
  • the resolved locale remains the single React/Intl authority
  • auto -> zh remains unchanged until the PR 4 completion gate
  • remaining specialized desktop surfaces, Follow system resolution, and the final bilingual E2E matrix remain PR 4
  • user content, model output, tool-owned payloads, brands, code, commands, paths, and generated content are not translated

Verification

Linux ext4 checkout, Node 24.18.0:

  • npm run lint
  • npm run build
  • npm run typecheck
  • npx knip --workspace apps/desktop
  • npx knip --workspace packages/ui
  • npm run build:test
  • Linux sandbox smoke: 5/5
  • npm run test:dist: all workspaces passed; desktop 2701/2701
  • localized conversation/tool focused suites: 15/15
  • workbar E2E contract: passed in isolation
  • alignment audit: all fixtures clean

Local WSL/Xvfb runs throttle requestAnimationFrame to ~2 seconds in the two long-scroll geometry tests. The same two timeouts reproduce on unmodified upstream main with identical 31.6k DOM height, 19 steps, 38 frames, and ~31 second deadline; GitHub's hosted E2E job remains the authoritative gate.

Part of #1052.

@Astro-HanAstro-Han 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.

Approved. Two non-blocking localization follow-ups:

  • In the English permission prompt, the stale-request age combines localized wrapper copy with the Chinese-only formatPermissionRequestWait(), so a request older than two minutes renders text such as Waiting for 6 分钟. The duration formatter should become locale-aware.
  • Collapsed session groups still wrap counts in hard-coded full-width parentheses, producing English labels such as Archived(3). Moving the count format into the catalog would keep typography locale-correct.

Neither issue blocks this PR. The catalog ownership, reactive locale wiring, and focused coverage look good.

…ion-session-localization
# Conflicts:
#	packages/ui/src/tool-activity/agent-preview.tsx
@Mingqwqqaq
Mingqwqqaqforce-pushed the feat/1052-conversation-session-localization branch from a07353b to 90bda8bCompareJuly 18, 2026 14:53
@Astro-Han
Astro-Han merged commit b2daec5 into apache:mainJul 18, 2026
3 checks passed
GabrielDrapor added a commit to GabrielDrapor/maka-agent that referenced this pull request Aug 5, 2026
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(apache#1194's `jumpLatest`, dropped by apache#1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
Astro-Han pushed a commit that referenced this pull request Aug 5, 2026
* fix(ui): localize the Astryx chrome adopted since #1795#1795 moved the chat surfaces onto Astryx ChatLayout and deleted the
`label={copy.jumpLatest}` that fed Maka's own scroll-to-bottom button,
but never registered the replacement `@astryx.*` keys. Astryx ships no
zh catalog, so the pill has been reading "New messages" in a Chinese UI
ever since — and the same gap covers every other Astryx surface adopted
without its copy.
Audited all 219 shipped Astryx keys against the components Maka actually
renders (JSX usage, not imports — the barrel re-export makes every
component look reachable). 79 rendered keys had no override; this adds
them, reusing existing entries where the string is identical rather than
duplicating (form.clear, primitives.close/loading, selectPlaceholder).
Excluded as dead config per this file's own rule: dateInput.dialogLabel
and dateInput.placeholder — DateTimeInput references neither, and no
standalone DateInput is rendered.
The guard test listed `chat` as a surface Maka does not render, which is
what let the regression through; it now guards lightbox only, and a new
test pins the chat chrome.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): keep 跳到最新消息 for the scroll-to-bottom pill
Astryx expands the pill only when new messages arrived, so the label
reads as a notice rather than an action — but the copy this replaced
(#1194's `jumpLatest`, dropped by #1795) said 跳到最新消息, and keeping
the wording users already know beats a semantically tidier rename.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(ui): assert the pinned chat overrides exist before checking them
`messages[key] ?? ''` coalesced a deleted override to the empty string,
which holds no Latin letters and satisfied the translation check on its
own — so the loop caught an English value but not a removed key.
Verified both ways: deleting the send override now fails with 'missing
override', and reverting its copy to English fails with 'untranslated'.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): localize the Lightbox reached via useLightbox
The dead-config guard's surviving entry was as stale as the one #1795
invalidated: chat-turn.tsx opens image previews through useLightbox,
which renders Astryx's Lightbox — with four English strings — while a
JSX-tag scan for <Lightbox> reports the component unrendered.
Add the four overrides (close reuses primitives.close) and retire the
ban-list guard entirely: a list keyed to "what we render today" rots
silently, and both of its entries had. Live surfaces are pinned by
presence tests instead.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* test(e2e): match the quote token remove button by its localized name
The token remove button read 'Remove {label}' only because Astryx's
Token had no zh override; now that @astryx.token.remove is localized the
accessible name is 移除…, and the /^Remove / locator times out. The spec
already addresses every other control by its Chinese name.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): address review — off-barrel Astryx copy, localize #642 fallback
Review findings on #2202, all verified before fixing:
- The astryx catalogue expanded SharedUiCopy, which `export *`s through
the package barrel — violating the README's off-barrel convention for
symbols with no cross-package consumer. It now lives in astryx-copy.ts,
deliberately unexported.
- The en mirror existed only as a diffable reference and was never
applied (astryxMessageOverrides returns undefined for en); dropped
rather than left to drift against upstream.
- `a` renamed to `astryx`; duplicated presence/Latin-letter assertions
extracted into assertChineseAstryxOverrides.
- P1: the #642 streaming fallback rendered a bare ChatMessage, which
resolves Astryx's shipped 'Message from {sender}' — an English
aria-label in a Chinese tree. It now reuses LocalizedChatMessage with
the same assistantAriaLabel as the TurnView path. The regression test
pins the exact trigger: wait indicators with zero turns — passing a
liveTurn instead projects a real turn and takes the localized path,
which is how the first draft of the test passed against the bug.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
* fix(ui): prune dead overrides, sweep the whole zh map in tests
Maintainer review on #2202, each claim verified before acting:
- chatSendButton.send/stop: ChatComposerInput never renders
ChatSendButton — composer.tsx supplies its own send button. Pruned;
the pinned test now anchors on chatToolCalls.error instead.
- appShell.mobileNavigation: gated on shouldShowAutoToggle, and Maka
passes breakpoint 'none' + hasToggle false. Pruned.
- table.noData / table.filter.* / tableFiltering.filterByColumn: the
usage table mounts only the rowHeader plugin and renders EmptyState
before Table when rows are empty. Pruned.
- New whole-map sweep: every override must target a key Astryx ships,
hold no Latin outside {…}, and carry the same top-level ICU arguments
as the en default — depth-aware extraction, since plural branch text
({result}) reads like an argument to a naive regex and would flag
every zh string that drops an inapplicable plural.
- Presence-helper comment reworded: the check reads the override map
directly, so a deleted entry yields '' here; at runtime the same miss
falls back to Astryx's shipped en catalog.
Sweep red-validated both ways: a misspelled key fails membership, an
English value fails the Latin check.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ac5rv6WUKWtMZgQb5QPN16
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto 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.

2 participants

@Mingqwqqaq@Astro-Han