Skip to content

feat(rest,spec): 导出文件名本地化 + 系统字段标签内置多语言回退#2911

Merged
os-zhuang merged 2 commits into
mainfrom
claude/export-filename-optimization-5dd15f
Jul 14, 2026
Merged

feat(rest,spec): 导出文件名本地化 + 系统字段标签内置多语言回退#2911
os-zhuang merged 2 commits into
mainfrom
claude/export-filename-optimization-5dd15f

Conversation

@baozhoutao

@baozhoutao baozhoutao commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

背景

用户反馈两处问题:

  1. 导出功能(CSV/Excel)文件名:直接下载得到 contracts.csv 这种裸 API 名,希望优化为「对象显示名-时间戳」。
  2. 导入模板仍有英文:下载模板的 CSV 里,注入的系统字段表头是英文(如 "Owner"),示例行是 ASCII slug(如 prepare)。

前端配套 PR:objectui(导出文件名前端兜底、导入模板示例值取显示标签、模板文件名本地化)。

变更

@objectstack/rest — 导出下载文件名

  • GET /data/:object/exportContent-Disposition 改为「对象显示名-时间戳」,按 RFC 5987/6266 双写:filename="task-20260714-153045.xlsx"(ASCII 兜底)+ filename*=UTF-8''任务-20260714-153045.xlsx(本地化标签)。
  • 新增 exportContentDisposition(objectName, label, ext, now?)

@objectstack/spec — 系统字段标签内置回退

  • ObjectQL 注册表注入的系统字段(owner_id/created_at/created_by/updated_at/updated_by)只有英文标签,自定义对象没有对应翻译条目,导致中文界面漏出 "Owner"/"Created At"。
  • translateObject 内置这五个字段的 en/zh-CN/ja-JP/es-ES 标签表(措辞与平台翻译包一致),仅当字段标签仍是注入的英文默认值时套用,作者自定义标签绝不覆盖;无翻译包时也生效。
  • REST 元数据翻译路径同步放宽(不再因缺 bundle 提前返回);缓存 ETag 本就按 locale 分键,无串味风险。

@objectstack/plugin-reports — 附件文件名

  • 定时报表附件文件名清洗从「非 ASCII 全替换 _」改为按 Unicode 字母/数字保留(\p{L}\p{N}),中文计划名不再变成下划线串。

showcase

  • 任务对象 In Progress 视图开启 exportOptions: ['csv','xlsx','json'],示例应用可直接演示/实测导出。

测试

  • 单测:spec 6756 通过、rest 260 通过(含 exportContentDisposition 5 例、系统字段回退 6 例)、plugin-reports 29 通过。
  • 真机实测(showcase dev 栈 + objectui console,中文界面):
    • curl -H 'Accept-Language: zh-CN' …/export?format=xlsxcontent-disposition: attachment; filename="showcase_task-20260714-063010.xlsx"; filename*=UTF-8''%E4%BB%BB%E5%8A%A1-…(即 任务-….xlsx);en-US → Task-….csv
    • 控制台 UI 导出 CSV/XLSX,浏览器实际落盘名:任务-20260714-064550.csv任务-20260714-064607.xlsx
    • 导入向导「下载模板」:模板首列表头由 "Owner" 变为「所有者」,created_at 显示「创建时间」。

追加(6ab3e7c):导出↔导入选项标签闭环

第二轮实测发现平台级缺陷:导出/模板写出的是翻译后的选项标签(如 待规划),但导入 coercion 只认作者原始标签——用户把自己刚导出的中文文件原样导回,select 字段全报 invalid_option

  • prepareImportRequest 新增 localizeSchema 钩子;REST 两处导入路由传 translateMetaItem,把翻译标签并入选项匹配同义词(mergeLocalizedOptionSynonyms)。
  • 作者标签、选项 code 照常接受;非法值照常报 invalid_option;钩子抛错时静默降级为原 schema。
  • showcase 补全 showcase_task en/zh-CN 翻译包(status/priority/done/labels/sync_status/sync_error),消除模板残留英文。
  • 单测 rest 265/265(+5 例 import-prepare.test.ts);真机 dry-run:中文标签导回 ok:1、英文标签不回归、非法值仍拦截(详见 PR 评论实测报告)。

Closes #2916

@vercel

vercel Bot commented Jul 14, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 14, 2026 12:02pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation protocol:system tests tooling size/m and removed documentation Improvements or additions to documentation protocol:system tests tooling labels Jul 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/plugin-reports, @objectstack/rest, @objectstack/spec.

97 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/rest, @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/rest, @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/rest, @objectstack/spec)
  • content/docs/automation/approvals.mdx (via packages/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/cli.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via packages/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via packages/rest, @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/rest, @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/plugin-reports, @objectstack/rest, @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via packages/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via packages/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx (via @objectstack/rest, @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/rest, @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@baozhoutao

Copy link
Copy Markdown
Contributor Author

真机 UI 复测报告(第二轮:「下载模板结果中还是有英文」)

环境:objectstack dev :3000(showcase,--seed-admin)+ objectui console Vite :5180,zh-CN 会话,浏览器实测(hook HTMLAnchorElement.click / URL.createObjectURL 抓真实下载物)。

1. 导入模板 — 全中文 ✅

UI 路径:任务 → In Progress 视图 → 导入 → 下载模板。实际下载:

  • 文件名:任务-导入模板.csv
  • 内容(真实 blob 原文):
所有者,标题 *,项目 *,负责人,状态 *,优先级,预计工时,进度,已完成,截止日期,开始日期,结束日期,创建时间,工作地点,封面,标签,备注,同步状态,同步错误
,,,,待规划,,0,,true,2024-01-31,2024-01-31,2024-01-31,2024-01-31 09:00,,,,,已同步,

第一轮遗留的英文(Done/Labels/Sync Status/Sync Error 表头,Backlog/Low/Synced 示例值)全部消除 —— 根因是 showcase 翻译包缺条目,已补全 en/zh-CN 两侧。

2. 导出 — 文件名 + 单元格全中文 ✅

UI 路径:同视图 → 导出 → 导出为 CSV。实际下载:

  • 文件名:任务-20260714-075156.csv(对象显示名-时间戳)
  • 内容:
标题,项目,负责人,状态,优先级,截止日期
Build homepage,Website Relaunch,sam@example.com,进行中,,2026-07-24
Ingest pipeline,Data Platform,linus@example.com,进行中,紧急,2026-08-03

3. 导出↔导入闭环(本次新发现并修复的平台缺陷)✅

导出/模板写出的是翻译后的选项标签,但导入 coercion 只认作者原始标签 → 把自己导出的中文文件导回去,select 字段全报 invalid_option。修复(6ab3e7c):prepareImportRequest 新增 localizeSchema 钩子,REST 导入路由传 translateMetaItem,翻译标签并入选项同义词。

实测(dry-run POST /api/v1/data/showcase_task/import,Accept-Language: zh-CN):

输入 结果
中文往返测试,Website Relaunch,待规划,紧急(翻译标签) {"total":1,"ok":1,"errors":0,"created":1}
无locale英文标签,Backlog(作者标签,无 locale) ok:1 ✅ 不回归
非法值仍报错,不存在的状态 invalid_option: "不存在的状态" is not a known option ✅ 仍拦截

4. 测试

@objectstack/rest 265/265 通过(+5 个新 import-prepare.test.ts:同义词合并、en no-op、带/不带 localizeSchema、钩子抛错降级)。

操作截图(导入对话框、In Progress 网格中文状态列)已在开发会话中留存。

…abel fallback

- rest: GET /data/:object/export Content-Disposition now uses the object's
  display label + timestamp via RFC 5987/6266 (ASCII api-name fallback in
  filename=, localized label in filename*=UTF-8''). New exportContentDisposition().
- spec: translateObject gains built-in en/zh-CN/ja-JP/es-ES labels for the
  five registry-injected system fields (owner_id/created_at/created_by/
  updated_at/updated_by), applied only when the label is still the injected
  English default; works without a translation bundle. REST meta translation
  no longer bails out when the bundle is missing (ETag is already locale-keyed).
- plugin-reports: attachment filename sanitizer keeps Unicode letters/digits
  instead of flattening CJK to underscores.
- showcase: enable exportOptions on the task In Progress view so export is
  demonstrated (and UI-testable) in the example app.
@os-zhuang
os-zhuang merged commit 607aaf4 into main Jul 14, 2026
17 checks passed
@os-zhuang
os-zhuang deleted the claude/export-filename-optimization-5dd15f branch July 14, 2026 14:11
…mport round-trip)

The localized export and the import template surface translated option
labels (e.g. 待规划 for backlog), but import coercion only knew the
authored schema labels — re-importing your own localized export failed
every select field with invalid_option.

- prepareImportRequest gains a localizeSchema hook; both REST import
  routes pass translateMetaItem so the request locale's labels merge
  into the field metaMap as matching synonyms (authored labels and
  option codes keep working; unknown values still fail; a throwing
  hook degrades to authored-only matching)
- new mergeLocalizedOptionSynonyms export + import-prepare tests
- app-showcase: complete the showcase_task en/zh-CN translation bundle
  (done/labels/sync_status/sync_error labels, status/priority/sync
  option maps) so templates and exports render fully localized
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:system size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

导出/导入文件名与多语言优化:显示名+视图名+时间戳命名、模板全中文、导出↔导入选项标签闭环

2 participants