From 9aaa53cbeac679248bab3aa7a2b10e3d5a7a4cb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F-Relakkes?= Date: Sun, 4 Oct 2026 22:22:13 +0800 Subject: [PATCH] fix(desktop): silence missing images and translate hardcoded text (#1450) * fix(desktop): show no error for images that are not there A reply named the screenshots it had taken as `/tmp/cc-haha-ui-review/0*.png`, and a red "Unable to load image" tile with a retry button appeared under it. The inline gallery took the glob for a file: its absolute-path pattern accepted any character but whitespace and quotes. Wildcards and substitutions (`*`, `?`, `{name}`, `${id}`, `$NAME`, `%03d`) now end a path there. Brackets stay, since real directories use them. The tile was the larger problem. In the local session history we checked, 45 of the 115 pictures the gallery tried to show were red tiles and only 18 existed: files cleaned out of /tmp, outputs deleted since, web routes, example paths. A retry fixes none of them. Every failure went red because an error carries no status, but the authenticated fetch that follows it does. A 400, 403, 404 or 413 from the file routes now means the picture is not there to show: the inline tile disappears, and a Markdown image falls back to its alt text. A server fault, a dropped connection, a refused credential or bytes that do not decode still show the retryable error, whose hint now names those causes. The guessed-name versus spelled-out-path split from #1429 goes: why the load failed decides, not how the path was written. A server test pins the 404 for a file missing from an allowed root, which the rule relies on. * fix(desktop): translate text typed into components The header over the pictures in a reply read "1 IMAGE" in every locale. The text was typed into JSX, and the type of the locale files only proves that every key has a translation, not that components use one. A scan of the 246 components found more: Mermaid's error, loading text and preview hint, "Untitled" in the sidebar, Computer Use's "Failed to check status.", token counts in /context, labels in the trajectory view, and screen-reader labels such as the sidebar region and the diff grid. They now go through t(), reusing a key where one existed and adding 15 to all five locales. The English text is unchanged. untranslatedText.test.ts keeps it that way. It reads every component and fails on JSX text, text-bearing attributes and string literals rendered as children that skip t(). Key caps, code, icon ligatures and t() fallbacks are not prose. Product names, units and example values are listed per file with the reason, and a listed text the source no longer has fails too. Modal's "Close dialog" stays a known gap: a ui primitive cannot read the locale, so the label has to come from its callers. --- .../src/__tests__/untranslatedText.test.ts | 223 ++++++++++++++++++ .../src/components/chat/AuthedImage.test.tsx | 32 +++ desktop/src/components/chat/AuthedImage.tsx | 6 +- .../chat/InlineImageGallery.test.tsx | 203 ++++++++++++---- .../components/chat/InlineImageGallery.tsx | 64 +++-- .../chat/LocalSlashCommandPanel.tsx | 8 +- .../src/components/chat/MermaidRenderer.tsx | 12 +- .../components/chat/ProjectContextChip.tsx | 2 +- desktop/src/components/chat/ToolCallGroup.tsx | 2 +- desktop/src/components/layout/Sidebar.tsx | 8 +- .../components/markdown/MarkdownHtml.test.tsx | 53 ++++- .../src/components/markdown/MarkdownHtml.tsx | 17 +- .../MarkdownRenderer.authedImage.test.tsx | 30 ++- .../trajectory/detail/MessageBlocks.tsx | 3 +- .../trajectory/detail/RawRequestDetail.tsx | 2 +- .../workspace/WorkspaceDiffSurface.tsx | 2 +- desktop/src/i18n/locales/en.ts | 17 +- desktop/src/i18n/locales/jp.ts | 17 +- desktop/src/i18n/locales/kr.ts | 17 +- desktop/src/i18n/locales/zh-TW.ts | 17 +- desktop/src/i18n/locales/zh.ts | 17 +- desktop/src/lib/useAuthedImageFallback.ts | 38 ++- desktop/src/pages/ActivitySettings.tsx | 2 +- desktop/src/pages/ComputerUseSettings.tsx | 6 +- .../src/pages/settings/ProviderSettings.tsx | 2 +- src/server/__tests__/filesystem.test.ts | 16 ++ 26 files changed, 684 insertions(+), 132 deletions(-) create mode 100644 desktop/src/__tests__/untranslatedText.test.ts diff --git a/desktop/src/__tests__/untranslatedText.test.ts b/desktop/src/__tests__/untranslatedText.test.ts new file mode 100644 index 00000000..ba5f8751 --- /dev/null +++ b/desktop/src/__tests__/untranslatedText.test.ts @@ -0,0 +1,223 @@ +// @vitest-environment node +import { readdirSync, readFileSync } from 'node:fs' +import path from 'node:path' +import ts from 'typescript' +import { describe, expect, it } from 'vitest' + +/** + * Text a user reads has to come from the locale files. A string typed straight + * into JSX renders the same in every locale, and nothing else notices: the + * "1 image" header over the pictures in a reply stayed English in the Chinese UI. + * + * So every component is read here. JSX text, the attributes a user reads or a + * screen reader speaks, and string literals rendered as children all have to go + * through `t()`. Text inside , ,
 or