Skip to content

fix(plugin-gantt): 快速筛选栏改走 i18n 词条,不再硬编码中文 - #3386

Merged
yinlianghui merged 1 commit into
mainfrom
claude/issue-5427-gantt-quickfilter-i18n
Aug 5, 2026
Merged

fix(plugin-gantt): 快速筛选栏改走 i18n 词条,不再硬编码中文#3386
yinlianghui merged 1 commit into
mainfrom
claude/issue-5427-gantt-quickfilter-i18n

Conversation

@yinlianghui

Copy link
Copy Markdown
Collaborator

Fixes objectstack-ai/objectstack#5427

问题

ObjectGantt 渲染 QuickFilterBar 时,把 all / clear / empty / resultSummary 四个 label 直接写成了中文字面量。两个缺陷叠在一处:

  1. 违反 AGENTS.md 诫条 #-1(英文码库 —— 所有用户可见文案必须是英文);
  2. 不可翻译 —— 它们不是词条键,所以在 en / ja / es / ar 会话下这一条筛选栏依然是中文,而甘特图工具栏其余部分本地化正常,反差非常显眼。

QuickFilterBar 本身无责:它是纯展示组件,且已经回退英文(labels?.clear ?? 'Clear')。是宿主钉死了文案。

前提核实

已对 origin/main 核实四处硬编码仍在(packages/plugin-gantt/src/ObjectGantt.tsx,call site 位于第 1384 行 QuickFilterBar),premise 成立。

改动

  • 四个文案移入新的 gantt.quickFilter 命名空间,十个语言包全部补齐(all-locales-key-parity 全量强制)。
  • 调用点改用 plugin-gantt 既有的 useGanttTranslation

关于第二点的取舍:issue 正文建议照 DashboardFilterBaruseSafeTranslate,但 ObjectGantt 组件内已经持有 const { t } = useGanttTranslation();(第 433 行),该包其余四十余条文案统统走它。再引入第二个翻译 hook 会让同一组件里并存两套机制;而 useGanttTranslation 的语义与 useSafeTranslate 等价(逐键回退,宿主词典缺键时回落内置英文而非裸键),且英文默认值集中在 GANTT_DEFAULT_TRANSLATIONS 这一张表里 —— 把这四条散到调用点反而会让"gantt 到底内置了哪些英文"这个问题失去单一答案。因此额外动了 useGanttTranslation.ts 一个文件(+7 行,登记四条英文默认值);不登记的话,无 Provider 的独立嵌入会把裸键 gantt.quickFilter.all 显示给用户。

gantt.quickFilter.resultSummary 刻意保持单花括号占位符({shown} / {total}):调用点用字面 .replace 替换,不是 i18next 插值,与 gantt.autoScheduleDlg.body 一致,也正是 all-locales-key-paritySINGLE 正则已知的约定。改成双花括号会把占位符原样显示给用户 —— 新增的 parity 测试对十个包逐一钉住了这一点。

测试

新增三个文件:

  • ObjectGantt.quickfilter.i18n.test.tsx —— en / zh 两面,各自带反向字面量断言(en 会话下断言中文四条都不出现;zh 会话下断言英文不泄漏),防硬编码悄悄回来。
  • ObjectGantt.quickfilter.i18n.fallback.test.tsx —— 无 Provider 时回落内置英文,断言绝不出现裸键、占位符必须被替换。单独成文件是必需的:挂过 I18nProvider 会把 react-i18next 的全局实例留在该语言上,同文件内的"无 Provider"渲染会继承它(实测拿到的是中文而非英文默认值)。
  • gantt-quickfilter-locale-parity.test.ts —— 十包覆盖 + 单花括号拼写 + 非 zh 包不得含被删除的中文字面量。

同时给 renderIn 显式传了 persistLanguage={false}:provider 的 bootstrap 会从 localStorage 读取上次持久化的语言并覆盖 defaultLanguage(见 resolveBootstrapConfig),不关掉的话 zh 用例会决定 en 用例渲染成什么,变成顺序依赖。

反向验证(先定方向再跑):把中文字面量还原回调用点后,预期 en 用例与无 Provider 用例转红、zh 用例仍然绿(硬编码的中文恰好等于 zh 文案)、locale parity 仍然绿(它守的是语言包,不是调用点)。实跑与预期完全一致 —— 这说明 zh 那一面单独存在时抓不到这个缺陷,en 一面才是承重的,两面同时保留是有意义的。

× ObjectGantt.quickfilter.i18n.fallback.test.tsx > 无 Provider  → expected '全部' to be 'All'
× ObjectGantt.quickfilter.i18n.test.tsx > en session            → expected '全部' to be 'All'
✓ ObjectGantt.quickfilter.i18n.test.tsx > zh session
✓ gantt-quickfilter-locale-parity.test.ts (全部)

修复态全绿:

pnpm vitest run packages/plugin-gantt/ packages/i18n/
Test Files  58 passed (58)
     Tests  544 passed (544)

pnpm type-check
Tasks:    78 successful, 78 total

changeset 已附(patch;按仓库约定不声明 major),check-changeset-no-major.mjs 通过。

未做的事

QuickFilterBar.tsx 第 41/43 行的 JSDoc 注释里仍有中文(/** "全部" / "All" — … */),同属诫条 #-1 但先于本 issue 存在、且与被渲染的文案无关,按"scope = issue"另行记录、不在本 PR 顺手改。

🤖 Generated with Claude Code

https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt


Generated by Claude Code

ObjectGantt 渲染 QuickFilterBar 时把 all/clear/empty/resultSummary 四个
label 直接写成中文字面量,导致 en/ja/es/ar 会话下这一条筛选栏仍是中文,
而甘特图工具栏其余部分正常本地化;同时违反 AGENTS.md 诫条 #-1(英文码库)。
QuickFilterBar 本身无责 —— 它是纯展示组件且已回退英文,是宿主钉死了文案。

四个文案移入新的 gantt.quickFilter 命名空间并补齐十个语言包,调用点改用
plugin-gantt 既有的 useGanttTranslation(与该包其余全部文案同一把 t,
逐键回退,宿主词典缺键时回落到内置英文而非裸键)。resultSummary 刻意保持
单花括号占位符({shown}/{total}):调用点用字面 .replace 替换而非 i18next
插值,与 gantt.autoScheduleDlg.body 一致,也正是 all-locales-key-parity
的 SINGLE 正则已知的约定 —— 改成 {{shown}} 会把占位符原样显示给用户。

Fixes objectstack-ai/objectstack#5427

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GTRjn8xBqp75dk7kFupVRt
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 5, 2026 12:08pm

Request Review

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Main entry (gzip) 28.1 KB 350 KB
Entry file index-DAi6VGXh.js
Status PASS

📦 Bundle Size Report

Package Size Gzipped
app-shell (index.js) 8.47KB 3.09KB
app-shell (runtime-config.js) 7.42KB 2.32KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 7.57KB 2.97KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 1.17KB 0.53KB
auth (AuthProvider.js) 22.10KB 4.37KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.13KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.64KB 2.21KB
auth (SocialSignInButtons.js) 9.60KB 3.89KB
auth (UserMenu.js) 3.40KB 1.22KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 35.76KB 9.11KB
auth (createAuthenticatedFetch.js) 4.37KB 1.69KB
auth (index.js) 2.35KB 1.07KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 4.91KB 0.87KB
auth (useIsWorkspaceAdmin.js) 1.61KB 0.85KB
collaboration (CommentThread.js) 18.38KB 4.49KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 3.65KB 1.42KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.25KB 0.53KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 477.32KB 104.75KB
core (index.js) 2.25KB 0.80KB
create-plugin (index.js) 9.28KB 2.98KB
data-objectstack (index.js) 136.23KB 34.75KB
fields (index.js) 227.18KB 55.72KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (i18n.js) 4.32KB 1.77KB
i18n (index.js) 2.65KB 1.06KB
i18n (pickLocalized.js) 1.70KB 0.83KB
i18n (provider.js) 9.48KB 3.27KB
i18n (useObjectLabel.js) 26.14KB 6.07KB
i18n (useSafeTranslation.js) 3.26KB 1.44KB
layout (index.js) 38.53KB 10.71KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.74KB
mobile (index.js) 1.50KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.71KB 0.42KB
mobile (useResponsiveConfig.js) 1.36KB 0.63KB
mobile (useSpecGesture.js) 4.05KB 1.53KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 8.75KB 3.06KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 3.67KB 1.12KB
permissions (evaluator.js) 4.41KB 1.44KB
permissions (index.js) 0.91KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.52KB
permissions (usePermissions.js) 1.55KB 0.71KB
plugin-ai (index.js) 15.71KB 3.79KB
plugin-calendar (index.js) 44.98KB 12.37KB
plugin-charts (index.js) 61.04KB 17.31KB
plugin-chatbot (index.js) 180.09KB 42.72KB
plugin-dashboard (index.js) 112.01KB 28.86KB
plugin-designer (index.js) 210.51KB 42.51KB
plugin-detail (index.js) 231.29KB 57.04KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 111.54KB 26.97KB
plugin-gantt (index.js) 162.55KB 39.57KB
plugin-grid (index.js) 185.08KB 49.04KB
plugin-kanban (index.js) 47.89KB 13.18KB
plugin-list (index.js) 105.02KB 25.36KB
plugin-map (index.js) 16.81KB 5.24KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 40.55KB 10.59KB
plugin-timeline (index.js) 25.76KB 7.33KB
plugin-tree (index.js) 8.34KB 2.82KB
plugin-view (index.js) 83.67KB 20.43KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.71KB 3.53KB
providers (index.js) 0.44KB 0.22KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.67KB 2.37KB
react (LazyPluginLoader.js) 3.77KB 1.33KB
react (SchemaRenderer.js) 19.28KB 6.38KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 1.02KB 0.55KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 4.09KB 1.74KB
sdui-parser (index.js) 4.47KB 2.03KB
sdui-parser (parse.js) 10.04KB 2.82KB
sdui-parser (types.js) 0.29KB 0.24KB
sdui-parser (validate.js) 4.69KB 1.48KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 0.20KB 0.18KB
types (crud.js) 0.20KB 0.18KB
types (data-display.js) 0.20KB 0.18KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.87KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-retry.js) 4.32KB 2.02KB
types (index.js) 2.46KB 1.21KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 0.20KB 0.18KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (spec-report.js) 5.05KB 1.93KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 0.20KB 0.18KB
types (ui-action.js) 3.40KB 1.71KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

@yinlianghui
yinlianghui marked this pull request as ready for review August 5, 2026 12:13
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit 5c856ec Aug 5, 2026
16 checks passed
@yinlianghui
yinlianghui deleted the claude/issue-5427-gantt-quickfilter-i18n branch August 5, 2026 12:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

objectui: ObjectGantt hardcodes Chinese quick-filter labels — English-only-codebase violation, and the gantt toolbar is Chinese in every locale

2 participants