Repository navigation
Commit 11c4eb5
feat(studio): a Chinese display name derives its identifier by pinyin at every Studio create door (#29734)
<!-- claude-projects-attribution: {"github_login":"zhuangjianguo"} -->
_Requested by **Steedos** · [project
thread](https://claude.ai/code/project/chan_018ETKaNwMZCNRYD7D8Q9Ubb?thread=cmsg_018ETKaNwMZCNRYD7D8Q9UbbJWK4KPmYk5DcrJTSxbJRJk)_
Fixes #29690
Clause-②: no
Metadata-protocol: none
Claim:
[6593397218](#29690 (comment))
· amendments
[6593439786](#29690 (comment)),
[6593495761](#29690 (comment))
## 维护者速读
- **改了什么**:Studio
里所有“由名称生成标识”的新建入口(新建软件包、新建对象等、添加字段、通用新建表单、新建视图、对象/报表/数据集设计器的 Name、字段 API
名、选项值),中文名称现在按词转成不带声调的拼音再生成标识:`维修中心` → `weixiu_zhongxin`,`客户等级` →
`kehu_dengji`。生成的标识照常显示、可改。
- **为什么改**:中文管理员用中文起名,过去每个入口都停下来要求手填英文标识,上手头十分钟要被拦好几次。
- **风险与回滚**:拼音字典 `pinyin-pro`
是按需加载的独立分包,首次遇到汉字才下载;加载失败时退回原来的“无法生成,请手填”。常驻包只多 0.9 KB(预算内)。回滚即还原本
PR,无数据迁移。
- **先看**:`romanizeHan.ts`(共享规则与加载状态);`StudioDesignSurface.tsx`
两行(添加字段预览名与实际创建名一致);`PackageFormDialog.tsx`(软件包 ID 与命名空间)。
- **两分钟验证**:`cd ui && pnpm exec vitest run
packages/app-shell/src/views/metadata-admin/romanizeHan.test.ts`,预期 9
条全绿(含多音字 `银行`→`yinhang`、`行动`→`xingdong`)。
- **需要你做的**:无,席位落地。
## What changes
One shared derivation,
`ui/packages/app-shell/src/views/metadata-admin/romanizeHan.ts`, sits in
front of each door's existing sanitizer. Latin text is untouched; each
Chinese word becomes its toneless pinyin, then the door applies the rule
it always applied (`toFieldNameLoose`, `toFieldNameOrEmpty`, `slugify`,
or the package id's `toIdSegment`). So the namespace prefix, the `__c`
suffix of a customization pair and the taken-name hint are unchanged;
they now see `weixiu_zhongxin` where they used to see an empty string.
- **Doors.** New package (`com.<org>.weixiu-zhongxin`, namespace
`weixiu_zhongxin`); the new-item dialog; Add field, where the popover's
preview and the host's created name both go through the helper, so the
name previewed is the name created; the generic create form
(`createDerive.ts`); New view; the object, report and dataset designers'
Name; a fresh field's API name in the field inspector; and a picklist
option's value. That last one was a silent loss: a Chinese option label
derived an empty value and the row was never saved.
- **Words, not syllables.** Word boundaries come from
`Intl.Segmenter('zh', { granularity: 'word' })`, and `pinyin-pro` reads
a polyphone by its word (`银行` → `yinhang`, `行动` → `xingdong`). `ü` is
written `v` (`绿色` → `lvse`). Without `Intl.Segmenter`, each character's
reading is joined by `_` (`ke_hu_deng_ji`).
- **Loading.** The dictionary is fetched the first time a door sees a
Han character. Until it lands, the door keeps the identifier empty,
keeps Create off, and holds back its "not derived" line
(`romanizerPending`). When it lands, a door whose identifier still
follows the name fills it in without another keystroke
(`useHanRomanizer` / `useWhenRomanizerLands`). The fill runs once, on
the render where the dictionary landed, so a person who clears the
identifier to type their own is never refilled. If the chunk cannot be
fetched, the load settles as failed, one `console.warn` says so, and
every door falls back to its "not derived" line and a hand-typed
identifier, as before this PR.
- **Japanese.** Text containing kana is left alone, so a Japanese name
still asks for the identifier. The existing pins that asserted "a
Chinese name derives nothing" now assert the same invariant with a kana
sample.
- **Package dialog subtitle.** The subtitle says "Give it a name to
start; everything else is filled in for you." That promise now holds for
a Chinese name. It still does not hold for a Japanese one, which opens
the advanced section with the hint, as before. The copy is unchanged.
**Bundle choice.** The client derives the pinyin itself, from
`pinyin-pro` loaded as a lazy chunk. It is the library the server's
pinyin search plugin already uses and is already in the lockfile, and it
reads polyphones by word. I rejected a server route because it would add
a REST route for a create-time preview, and `plugin-pinyin-search` runs
only behind `OS_SEARCH_PINYIN_ENABLED`. I rejected the collator-sort
libraries because they read polyphones wrong.
`pnpm --dir ui check:eager-closure`:
| | eager closure (gz) | eager chunks |
|:--|--:|--:|
| base `1c1a5d40` | 2092.9 KB | 100 of 2397 |
| this branch before the main merge | 2093.8 KB | 101 of 2399 |
| final head (after merging main) | 2094.3 KB | 101 of 2399 |
The last row's extra 0.5 KB is main's own change in
`vendor-objectstack`, which went from 573.1 to 573.6 KB. The ceiling and
baseline are untouched, and headroom is 337.3 KB. The dictionary chunk
is `assets/esm-Cst8rBvH.js`: 286.6 KB raw, 141.7 KB gzipped, and lazy.
The helper itself, about 0.9 KB, is in the eager closure because
`PackageFormDialog` is eager. The seat accepted this on the card thread.
## Tests
New:
- `romanizeHan.test.ts`
- Before load: text unchanged and pending.
- After load: `weixiu_zhongxin` and `kehu_dengji` through every door
sanitizer, plus the polyphone pair, `lvse`, mixed `维修Center 2号` →
`weixiu_center_2_hao`, and kana unchanged.
- With `Intl.Segmenter` absent: `ke_hu_deng_ji` and `yin_hang`.
- `romanizeHan.loadFailure.test.tsx`
- The chunk import rejects, the load settles, nothing is pending, and
one warning is logged.
- `CreateItemDialog` shows its "not derived" line, and a hand-typed id
submits.
- `romanizeHan.lands-29690.test.tsx`
- `CreateItemDialog` stays empty and quiet with Create off while
loading, then shows `weixiu_zhongxin` with Create on.
- `createDerive.romanizerLands-29690.test.tsx`
- The generic create form is pending, then `kehu_dengji` with nothing
underivable.
- `ReportDefaultInspector.romanizerLands-29690.test.tsx`
- The designer Name fills on landing.
- A cleared Name is not refilled.
- `CreateViewDialog.pinyinName-29690.test.tsx`
- `客户列表` → `kehu_liebiao`, and the name is editable.
- A kana label derives nothing.
Extended with a zh-CN case each:
- `CreateItemDialog.notDerived-13389`: the namespace prefix gives
`repairs_weixiu_zhongxin`.
- `PackageFormDialog.idDerive-11792`: org `acme` gives
`com.acme.weixiu-zhongxin` and namespace `weixiu_zhongxin` in the posted
manifest; the id stays editable.
- `DataPillar.addFieldPopover-12516`: the preview `kehu_dengji` equals
the created field; a second `客户等级` gets the taken-name hint.
- `DataPillar.baseObjectExtension-28575`: the preview `kehu_dengji__c`
equals the extension field saved.
- `ResourceEditPage.underivableName-28232`
- `Report…`/`DatasetDefaultInspector.labelLeavesNameEmpty-12789`
- `ObjectDefaultInspector`
- `ObjectFieldInspector.apiNameSync`
- `ObjectFieldInspector.optionLabelFirst-27521`: `进行中`/`已完成` →
`jinxing_zhong`/`yi_wancheng` saved, and the field parses.
Kana samples replace the Chinese ones in
`PackageFormDialog.derivedFields-12512`,
`PackageFormDialog.nameOnlyFirstStep-27833` and
`ResourceEditPage.identifierIssueI18n-28369`.
Ablations went through `scripts/ablation-replace.mjs`. Each one went red
and was restored to HEAD:
| mutation | result |
|:--|:--|
| `romanizeHan` returns its input | 8 of 16 red |
| the Add-field host derives without pinyin | 2 of 12 red, preview ≠
created |
| the landing fill runs every render | 2 of 7 red, a cleared Name is
refilled |
## Acceptance notes
- A fresh field's API name in the field inspector derives on the label
keystroke after the dictionary has landed. It does not re-derive on
landing by itself: the inspector returns early before its row exists. In
practice the dictionary has already landed by then, because a field is
created through Add field, which loads it.
- Add field keeps parity with a Latin label. The derived name is shown
as the popover's preview, and the person edits it in the field inspector
after creating, as today.
- No zh/en catalogue rows changed. Every door's existing "not derived"
sentence still applies, and only to text the rule cannot read.
## Verification
All runs were under `os-verify-lock`.
<details><summary>Affected tests, type-check, lint</summary>
- `turbo`-affected app-shell test files: 308 files in 6 batches.
- The first run's reds were all pins asserting that a Chinese name
derives nothing, plus one real bug, the designer Name refilling after it
was cleared. Both are fixed above.
- The new and changed files re-ran 35 of 35 green (259 tests). The field
inspector files re-ran 31 of 31 green (324 tests).
- `pnpm exec turbo run type-check --filter=@object-ui/app-shell`: 65 of
65 tasks pass.
- `eslint` on the changed files: 0 errors.
</details>
<details><summary>Gates</summary>
`node scripts/pm/dispatch-gates.mjs --commands` derived 57 commands on
this diff, and `--ran` reconciled all 57. 56 exit 0.
`check:dual-build-cjs-loads` exits 3, because it needs every workspace
package built; it is left to CI. The exit codes are in the os-dev report
on the card.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01QsR2jTMu2EmYV84fwNrBXw)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 545c1c7 commit 11c4eb5
33 files changed
Lines changed: 1035 additions & 104 deletions
File tree
- .changeset
- ui/packages/app-shell
- src/views
- metadata-admin
- inspectors
- studio-design
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
| 98 | + | |
98 | 99 | | |
99 | 100 | | |
100 | 101 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
16 | | - | |
17 | | - | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
| |||
49 | 50 | | |
50 | 51 | | |
51 | 52 | | |
| 53 | + | |
52 | 54 | | |
53 | 55 | | |
54 | 56 | | |
| |||
518 | 520 | | |
519 | 521 | | |
520 | 522 | | |
521 | | - | |
522 | | - | |
523 | | - | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
| 527 | + | |
| 528 | + | |
524 | 529 | | |
525 | | - | |
526 | | - | |
| 530 | + | |
| 531 | + | |
527 | 532 | | |
528 | 533 | | |
529 | 534 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
152 | 152 | | |
153 | 153 | | |
154 | 154 | | |
155 | | - | |
| 155 | + | |
| 156 | + | |
156 | 157 | | |
157 | 158 | | |
158 | 159 | | |
159 | 160 | | |
160 | 161 | | |
161 | 162 | | |
162 | 163 | | |
163 | | - | |
| 164 | + | |
164 | 165 | | |
165 | | - | |
| 166 | + | |
166 | 167 | | |
167 | 168 | | |
168 | 169 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
30 | 34 | | |
31 | 35 | | |
32 | 36 | | |
| |||
124 | 128 | | |
125 | 129 | | |
126 | 130 | | |
| 131 | + | |
| 132 | + | |
127 | 133 | | |
128 | 134 | | |
129 | | - | |
| 135 | + | |
130 | 136 | | |
131 | 137 | | |
132 | 138 | | |
| |||
214 | 220 | | |
215 | 221 | | |
216 | 222 | | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
153 | 153 | | |
154 | 154 | | |
155 | 155 | | |
156 | | - | |
| 156 | + | |
| 157 | + | |
157 | 158 | | |
158 | 159 | | |
159 | 160 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
| 63 | + | |
63 | 64 | | |
64 | 65 | | |
65 | 66 | | |
| |||
222 | 223 | | |
223 | 224 | | |
224 | 225 | | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
225 | 230 | | |
226 | 231 | | |
227 | | - | |
| 232 | + | |
228 | 233 | | |
229 | 234 | | |
230 | 235 | | |
| |||
481 | 486 | | |
482 | 487 | | |
483 | 488 | | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
484 | 493 | | |
485 | | - | |
486 | | - | |
| 494 | + | |
| 495 | + | |
487 | 496 | | |
488 | 497 | | |
489 | 498 | | |
| |||
495 | 504 | | |
496 | 505 | | |
497 | 506 | | |
498 | | - | |
| 507 | + | |
499 | 508 | | |
500 | 509 | | |
501 | 510 | | |
| |||
531 | 540 | | |
532 | 541 | | |
533 | 542 | | |
534 | | - | |
535 | | - | |
536 | | - | |
537 | | - | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
538 | 550 | | |
539 | | - | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
540 | 557 | | |
541 | 558 | | |
542 | 559 | | |
543 | 560 | | |
544 | | - | |
| 561 | + | |
545 | 562 | | |
546 | 563 | | |
547 | 564 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
7 | | - | |
8 | | - | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| |||
116 | 117 | | |
117 | 118 | | |
118 | 119 | | |
119 | | - | |
120 | | - | |
| 120 | + | |
| 121 | + | |
121 | 122 | | |
122 | 123 | | |
123 | 124 | | |
124 | 125 | | |
125 | 126 | | |
126 | 127 | | |
127 | | - | |
| 128 | + | |
128 | 129 | | |
129 | 130 | | |
130 | 131 | | |
| |||
139 | 140 | | |
140 | 141 | | |
141 | 142 | | |
142 | | - | |
| 143 | + | |
143 | 144 | | |
144 | 145 | | |
145 | 146 | | |
146 | 147 | | |
147 | 148 | | |
148 | 149 | | |
149 | 150 | | |
150 | | - | |
| 151 | + | |
151 | 152 | | |
152 | 153 | | |
153 | 154 | | |
154 | 155 | | |
155 | 156 | | |
156 | | - | |
| 157 | + | |
157 | 158 | | |
158 | 159 | | |
159 | 160 | | |
| |||
0 commit comments