diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index b2736c8..748b825 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -162,6 +162,8 @@ - [x] ~~布局塌陷~~(v0.2.1 修复:antd `` 包裹层 `.ant-app` 无高度导致百分比高度链断裂) - [x] ~~关闭标签页整窗崩溃~~(v0.2.1 修复:xterm IDisposable 对象被当函数调用;并新增 ErrorBoundary 兜底 + 渲染层报错转发主进程日志) - [x] ~~首启动空屏~~(v0.2.1:启动自动打开一个终端) +- [ ] zh-TW 用语统一(i18n 质量专项):`命令/指令` 全文件混用(panels/ssh/settings/terminal,台湾惯用「指令」)、`口令`→`密碼`(ssh.secret.passphrase)、顺带通读 服务器→伺服器 / 默认→預設 / 网络→網路 / 软件→軟體。需要一次全局连贯的专项,零散改会制造新的不一致 +- [ ] ja 片假名长音符号统一:`フォルダ`(6) / `フォルダー`(7) 混用,二选一全局统一 - [ ] autoWrap=false 的固定列宽模式(xterm.js 无原生 wrapMode,需自定义列数控制) - [ ] 终端标题跟随 OSC 序列(窗格标题显示当前目录/命令) - [ ] 内置等宽字体打包(OFL 许可:JetBrains Mono / Fira Code 等) diff --git a/src/renderer/src/ErrorBoundary.tsx b/src/renderer/src/ErrorBoundary.tsx index e806fc9..04d9f27 100644 --- a/src/renderer/src/ErrorBoundary.tsx +++ b/src/renderer/src/ErrorBoundary.tsx @@ -1,6 +1,7 @@ import { Component } from 'react' import type { ErrorInfo, ReactNode } from 'react' -import { t } from '@shared/i18n' +import { DEFAULT_LANGUAGE, tFor, type Language } from '@shared/i18n' +import { useSettingsStore } from './settings/store' interface ErrorBoundaryProps { children: ReactNode @@ -11,10 +12,29 @@ interface ErrorBoundaryState { } /** - * Last-resort boundary: a render/effect crash must never take the whole app - * down to a black window. Shows the error and a reload escape instead. + * Error boundaries must stay class components (they own the error lifecycle), + * and class components cannot subscribe to the settings store the way function + * components do — nor do they re-render when App re-renders on a language + * switch (the root boundary is App's *parent*). So the exported boundaries are + * thin function wrappers that subscribe to the language and hand it down as a + * prop; the classes translate through `tFor(language, …)`. Without this, a + * crashed panel showed its fallback screen in whatever language the app was + * launched in — the one screen where reading it matters most. */ -export class ErrorBoundary extends Component { +function useBoundaryLanguage(): Language { + return useSettingsStore((s) => s.settings.system.language ?? DEFAULT_LANGUAGE) +} + +/** Last-resort boundary: a render/effect crash must never take the whole app + * down to a black window. Shows the error and a reload escape instead. */ +export function ErrorBoundary(props: ErrorBoundaryProps): ReactNode { + const language = useBoundaryLanguage() + return +} + +type ErrorBoundaryClassProps = ErrorBoundaryProps & { language: Language } + +class ErrorBoundaryClass extends Component { state: ErrorBoundaryState = { error: null } static getDerivedStateFromError(error: Error): ErrorBoundaryState { @@ -39,7 +59,7 @@ export class ErrorBoundary extends Component -
{t('workspace.error.title')}
+
{tFor(this.props.language, 'workspace.error.title')}
 window.location.reload()}
             style={{ padding: '6px 18px', cursor: 'pointer' }}
           >
-            {t('workspace.error.reload')}
+            {tFor(this.props.language, 'workspace.error.reload')}
           
         
       )
@@ -78,7 +98,14 @@ interface PanelErrorBoundaryProps {
  * other live session. A crashed pane is replaced in place and can be remounted
  * on retry; the rest of the workspace is untouched.
  */
-export class PanelErrorBoundary extends Component {
+export function PanelErrorBoundary(props: PanelErrorBoundaryProps): ReactNode {
+  const language = useBoundaryLanguage()
+  return 
+}
+
+type PanelErrorBoundaryClassProps = PanelErrorBoundaryProps & { language: Language }
+
+class PanelErrorBoundaryClass extends Component {
   state: ErrorBoundaryState = { error: null }
 
   static getDerivedStateFromError(error: Error): ErrorBoundaryState {
@@ -98,10 +125,12 @@ export class PanelErrorBoundary extends Component
-        
{this.props.label ?? t('workspace.error.panelTitle')}
+
+ {this.props.label ?? tFor(this.props.language, 'workspace.error.panelTitle')} +
{this.state.error.message}
)