Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

[Studio i18n] 表单布局设计器未解析已有的对象字段和分组翻译 #3134

Closed

Description

@os-zhuang

复现

  1. HotCRM 的 zh-CN 翻译已声明 objects.crm_opportunity.fields.name.label = 商机名称,以及 objects.crm_opportunity._sections.basic.label = 基本信息。
  2. 以中文进入 Studio → 数据 → 商机 → 表单 → 布局。
  3. 画布仍显示 Opportunity Name 和 Basic Information;同一对象的常规数据视图会正确显示商机名称和基本信息。

这不是种子数据的翻译问题:翻译键存在,且运行态其他表面已能解析。

根因证据

ObjectFormDesigner 的职责说明称布局画布应当与最终用户表单一致,但它直接读取原始元数据:

  • SortableField 使用 entry.def.label 作为显示名称。
  • 分组标题使用 g.label 或 g.key。
  • 组件没有调用已经存在的 useObjectLabel().fieldLabel / sectionLabel。

因此,项目可以提供 objects. 的 fields 和 _sections 翻译,却无法让 Studio 的表单布局画布使用它们。

期望

  • 布局画布按当前语言解析 objects.._sections..label 与 objects..fields..label。
  • 找不到翻译时继续回退到元数据的英文 label。
  • 右侧属性编辑器仍可编辑原始元数据 label;展示值与被编辑的源值不应混淆。
  • 语言切换后画布应重新解析。
  • 为字段和分组各增加一个中文翻译的回归测试。
  • 影响

    中文 Studio 的表单设计截图和实际 WYSIWYG 预览呈现中英文混排;项目侧补全翻译无法解决。

Activity

  1. self-assigned this
    on Aug 1, 2026
  2. os-zhuang commented on Aug 6, 2026

    @os-zhuang
    ContributorAuthor

    分诊:pm:queue。 ⛔ 本仓不打 domain:*(objectui 整仓座位)。

    判级理由

    具体缺陷,三步复现 + 根因证据齐备。关键在于立单者已排除最容易误判的那个解释:「翻译键存在,且运行态其他表面已能解析」—— 同一对象的常规数据视图正确显示「商机名称 / 基本信息」,只有 Studio 表单布局画布显示英文原名 ⇒ 不是种子数据或翻译缺失,是设计器组件绕过了 i18n 解析层。

    根因指到了组件与函数:ObjectFormDesigner 的 SortableField 直接用 entry.def.label、分组标题直接用 g.label ?? g.key,都没有调用仓内已存在的 useObjectLabel().fieldLabel / sectionLabel。

    一句话验收标准:中文会话下,Studio 表单布局画布的字段名与分组标题与最终用户表单一致。

    落点锚定理由(⛔ 不按标题词汇猜)

    修复落在本仓 packages/app-shell 的 Studio 设计器组件(ObjectFormDesigner 及其 SortableField),改法是接上同仓已有的 useObjectLabel hook(origin/main 上该 hook 的消费方遍布 app-shell/src/console/**,即基础设施现成)。⇒ 纯本仓、纯消费侧接线,不跨仓、不触 packages/spec,不走跨座位转移。

    ⚠️ 正文被截断 —— 但不影响可派发性

    正文最后一句停在「因此,项目可以提供 objects.」处,疑似 sanitizer 或编辑意外截断。本轮判定不适用「只留修复指令、不打标签」那条规则:被截断的是结论段的收尾修辞,而复现、根因、涉及组件、期望行为在截断之前已全部给出,不缺可派发所需的任何要素。接手方⛔ 不必等正文补全。

    (若原作者手边还有被截掉的那段,欢迎补一条评论;但它不是阻塞项。)

    过时前提检查

    git -C /home/user/objectui fetch origin main 已跑,useObjectLabel 在 origin/main 的 app-shell/src/console/** 广泛在用,hook 健在。⚠️ 本单立于 2026-08-01,距今 5 天 ⇒ 派发前在当前 main 上重跑三步复现;Studio i18n 面近期有多单在动,不排除已被顺带修好。

    查重

    三仓搜 Studio i18n / 表单布局设计器 / useObjectLabel / _sections 翻译:无重复单。⚠️ 但同一 i18n 缺口极可能不止表单布局这一个设计器面 —— 若接手时发现列表/详情/看板设计器有同样的「直接读 def.label」写法,请独立立单(查重先行)并在本单交叉引用,⛔ 不要扩本单范围把整个 Studio 的 i18n 审计吞进来。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. os-support-ai commented on Aug 18, 2026

    @os-support-ai
    Collaborator

    Stale-claim probe from the repo:objectui PM seat (session session_01RV6yuVCxymHYE16PL9vQkE). ⛔ Not a claim, and nothing has been changed — the assignee stands.

    This card is assigned to os-zhuang and has been since 2026-08-06, twelve days ago. Against the reclaim criteria I can read from GitHub:

    • No claim comment. The only comment here is explicitly labelled a triage comment — "本评论来自分诊座位 Routine…不构成认领" — so there is no session ID, no promised branch, and no worktree on record.
    • No branch matching this issue number.
    • No PR.

    That is the shape of a stale claim. I am not acting on it unilaterally: the rule is that an assignee who is not me means the card is taken, whatever else the evidence says, and reclaiming is a comment-then-wait protocol, not a unilateral unassign.

    So this is the asking half. If the holder of os-zhuang is still working this, say so and it stays put. If there is no live work, I will remove the assignee after a silence window and return the card to the queue with a note recording why — it will not be reassigned to me by that act, just made claimable again.

    Why I am bothering: the maintainer's standing order for this seat is to prioritise bugs and user-visible UI/UX, and this is one — a Chinese-language user opening Studio → form layout sees Opportunity Name and Basic Information where every other surface for the same object correctly renders 商机名称 and 基本信息. Triage already located the root cause (ObjectFormDesigner's SortableField reading entry.def.label, and group titles reading g.label ?? g.key, none of them going through the existing useObjectLabel().fieldLabel / sectionLabel) and confirmed the hook is live on main. It is a well-specified, consumer-side wiring fix that has been parked for twelve days.

    Two notes for whoever ends up doing it, so they are not lost:

    1. Triage flagged the body as truncated — it stops mid-sentence at "因此,项目可以提供 objects." That was judged non-blocking because repro, root cause, components and expected behaviour all appear before the cut. Still worth knowing you are not missing a requirement.
    2. Triage's own warning about scope: the same i18n gap very likely exists on other designer surfaces (list / detail / kanban). If so, file separately with a cross-reference — ⛔ do not let this card swallow a whole Studio i18n audit.

    Also recorded: this card carries pm:queue and an assignee simultaneously, which is a half-state — the two together mean neither "dispatchable" nor "in flight" reads correctly off the labels. Flagging rather than fixing, since the assignee is not mine to interpret.


    Generated by Claude Code

  4. os-support-ai commented on Aug 18, 2026

    @os-support-ai
    Collaborator

    Closing — already fixed on main. Two actions in one comment, both by the repo:objectui PM seat, round 6, session session_01RV6yuVCxymHYE16PL9vQkE.

    1. Assignee removed (the stale-claim window expired)

    The probe on this card was posted 2026-08-18 14:23Z and asked the holder of os-zhuang to say so if the work was live. At the time of this comment that is 5.5 hours of silence, on top of the twelve days the assignment had already stood. Against the evidence readable from GitHub — no claim comment (the only os-zhuang comment here is explicitly labelled 「本评论来自分诊座位 Routine…不构成认领」), no branch, no PR — the claim is stale and the assignee is removed.

    Provenance for the unassign: acting on the seat's own probe protocol, per the reclaim criteria stated in that probe comment. It was not reassigned to me by that act, exactly as the probe promised.

    2. The card is already delivered — verified on origin/main

    I ran the premise check before dispatching, because triage's own comment warned that this card was filed 2026-08-01 and 「派发前在当前 main 上重跑三步复现」. That warning earned its keep: the fix is on main.

    packages/app-shell/src/views/studio-design/ObjectFormDesigner.tsx now resolves both label channels through the hook the card said was being bypassed:

    const { fieldLabel, sectionLabel } = useSafeFieldLabel();
    
    const fieldLabelOf = React.useCallback((entry: FieldEntry) => {
      const fallback = String(entry.def.label ?? entry.name);
      return objectName ? fieldLabel(objectName, entry.name, fallback) : fallback;
    }, [objectName, fieldLabel]);
    
    // …and for group titles:
    const fallback = g.label || g.key;
    m.set(cid(g.key), objectName ? sectionLabel(objectName, g.key, fallback) : fallback);
    

    entry.def.label and g.label still appear — which is exactly why a grep alone would have been misleading — but they are now the fallback arguments to fieldLabel / sectionLabel, not the displayed values. That is the wiring the card asked for.

    It also carries a named regression test, ObjectFormDesigner.test.tsx:

    describe('ObjectFormDesigner — object translations (objectui#3134)', …)
    

    whose header states the defect in the past tense — "It used to read entry.def.label and group.label straight off the draft metadata, so a fully translated object still rendered its English source labels in the designer" — and pins the crm_opportunity / Opportunity Name / Basic Information case this card reported, against a zh instance.

    The choice of useSafeFieldLabel (the provider-safe wrapper) over useObjectLabel directly is deliberate and documented in the source: the designer is also mounted in tests and previews with no I18nProvider, where it degrades to an identity fallback.

    Closing as completed. Whoever landed it did so without closing this card — the fix is real and pinned, so the card is finished regardless of which PR carried it.

    Note for the seat, recorded because it nearly cost a dispatch

    This is the third card today that was already fixed before I looked at it, and the first one I caught before spending a dev on it. The difference was mechanical, not attentiveness: triage had written "re-run the repro on current main before dispatching" into the card, and I ran it. #5217 had no such line and I dispatched it into an empty problem.

    The generalisation: a card's age is itself a premise-check trigger. This one was 17 days old; the fix landed somewhere in that window without closing it. Cards do not announce that they have gone stale — the only defence is measuring at dispatch time, every time.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions