@cardenelabs/cdl 0.57.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,6 @@
1
1
  import type { JSX } from "react";
2
2
  import type { LaidNode } from "../types";
3
+ import { 字引きを作る, type CdlLang } from "../lang";
3
4
 
4
5
  /**
5
6
  * Shape-driven software 6 kinds (CAR-1111 Phase 2-B)。
@@ -38,6 +39,8 @@ import type { LaidNode } from "../types";
38
39
  type ShapeProps = {
39
40
  node: LaidNode;
40
41
  active: boolean;
42
+ /** 描画側の固定の字の言語 (#847)。 渡さない呼び方は英語で描く */
43
+ lang?: CdlLang;
41
44
  };
42
45
 
43
46
  const commonFrame = (active: boolean) => ({
@@ -53,6 +56,19 @@ const DESCENT = 0.25;
53
56
  // SVG の字幅は描く前に測れず、 getBBox も描いた後にしか使えないため送り幅で見積もる。
54
57
  // 手元の等幅書体は 0.55-0.61 なので、 0.62 なら実寸を下回らず収めた行が実際にはみ出さない。
55
58
  const MONO_ADVANCE = 0.62;
59
+
60
+ /**
61
+ * 等幅の書体で描く字の桁数。 和文 (U+2E80 以降) は 2 桁、それ以外は 1 桁で数える (#847)。
62
+ *
63
+ * 字数 (`length`) で数えると、日本語の辞書で描く字 (`進行中` / `→ 12 個を 1.8 秒で出力`) を
64
+ * 半分の幅に見積もり、縮める倍率が効かずに箱からはみ出す。 英語の辞書の字はすべて U+2E80 より
65
+ * 前にあり (`→` も含む)、字数と同じ値になるので、英語の図の見え方は変わらない。
66
+ */
67
+ function 等幅の桁(s: string): number {
68
+ let 桁 = 0;
69
+ for (const c of s) 桁 += (c.codePointAt(0) ?? 0) >= 0x2e80 ? 2 : 1;
70
+ return 桁;
71
+ }
56
72
  // 説明は名前が 16 の時に従来の 11 となり、 名前を縮めた時も同じ比で追随する。
57
73
  const LABEL_SUB_RATIO = 11 / 16;
58
74
  // 肩書は名前が 16 の時に従来の 12 となり、 名前を縮めた時も同じ比で追随する。
@@ -187,7 +203,8 @@ export function ShapeWindowNode({ node, active }: ShapeProps): JSX.Element {
187
203
  }
188
204
 
189
205
  /** shape-terminal = 端末 (黒地 + macOS 風 top bar + prompt 3 行 + typing cursor) */
190
- export function ShapeTerminalNode({ node, active }: ShapeProps): JSX.Element {
206
+ export function ShapeTerminalNode({ node, active, lang }: ShapeProps): JSX.Element {
207
+ const 字引 = 字引きを作る(lang);
191
208
  const frame = commonFrame(active);
192
209
  const 線 = frame.strokeWidth / 2;
193
210
  const x = node.cx - node.w / 2 + 線;
@@ -209,11 +226,11 @@ export function ShapeTerminalNode({ node, active }: ShapeProps): JSX.Element {
209
226
  { 左: 16, 字: 12.5, 色: "#7cd88f", 文: "$" },
210
227
  { 左: 30, 字: 12.5, 色: "#e6edf3", 文: "pnpm build" },
211
228
  ],
212
- [{ 左: 16, 字: 12, 色: "#8b95a5", 文: "→ 12 chunks in 1.8s" }],
229
+ [{ 左: 16, 字: 12, 色: "#8b95a5", 文: 字引("terminal.output") }],
213
230
  [{ 左: 16, 字: 12.5, 色: "#7cd88f", 文: "$" }],
214
231
  ];
215
232
  // 中身は自分の座標で描くため、 本体に入る倍率を出せるよう最長行から自然幅を求める。
216
- const 自然幅 = Math.max(...行.flat().map((t) => t.左 + t.文.length * MONO_ADVANCE * t.字)) + 余白;
233
+ const 自然幅 = Math.max(...行.flat().map((c) => c.左 + 等幅の桁(c.文) * MONO_ADVANCE * c.字)) + 余白;
217
234
  // 最終行とカーソルまで入る自然高を使い、 縦にも同じ倍率を適用できるようにする。
218
235
  const 自然高 = 帯 + 先頭 + 行間 * (行.length - 1) + Math.max(カーソル.下, 12.5 * DESCENT);
219
236
  // 縦横を同じ倍率で縮めて比を保ち、 幅 300・高さ 200 では 1 のまま従来値を保つ。
@@ -317,7 +334,8 @@ export function ShapeTerminalNode({ node, active }: ShapeProps): JSX.Element {
317
334
  }
318
335
 
319
336
  /** shape-code-block = code editor (tab + gutter + 4 syntax-colored stub lines) */
320
- export function ShapeCodeBlockNode({ node, active }: ShapeProps): JSX.Element {
337
+ export function ShapeCodeBlockNode({ node, active, lang }: ShapeProps): JSX.Element {
338
+ const 字引 = 字引きを作る(lang);
321
339
  const frame = commonFrame(active);
322
340
  const 線 = frame.strokeWidth / 2;
323
341
  const x = node.cx - node.w / 2 + 線;
@@ -410,7 +428,7 @@ export function ShapeCodeBlockNode({ node, active }: ShapeProps): JSX.Element {
410
428
  textAnchor="middle"
411
429
  fill="#e6edf3"
412
430
  >
413
- utils.ts
431
+ {字引("codeBlock.fileName")}
414
432
  </text>
415
433
  <circle cx={x + tabW - 丸} cy={y + tabH / 2} r={3 * 倍} fill="#7cd88f" stroke="none" />
416
434
  <path
@@ -483,7 +501,8 @@ export function ShapeCodeBlockNode({ node, active }: ShapeProps): JSX.Element {
483
501
  }
484
502
 
485
503
  /** shape-kanban-card = kanban ticket (top color bar + 3 stub line) */
486
- export function ShapeKanbanCardNode({ node, active }: ShapeProps): JSX.Element {
504
+ export function ShapeKanbanCardNode({ node, active, lang }: ShapeProps): JSX.Element {
505
+ const 字引 = 字引きを作る(lang);
487
506
  const frame = commonFrame(active);
488
507
  const 線 = frame.strokeWidth / 2;
489
508
  // 影は札を右下へずらして重ねるため、 ずらす分と枠線を札の寸法から先に引く。
@@ -496,7 +515,7 @@ export function ShapeKanbanCardNode({ node, active }: ShapeProps): JSX.Element {
496
515
  const 左右余白 = 14;
497
516
  const 上段隙間 = 8;
498
517
  const 優先帯高 = 10;
499
- const ID文 = "CAR-1111";
518
+ const ID文 = 字引("kanbanCard.id");
500
519
  const ID基準 = 30;
501
520
  const ID字 = 11;
502
521
  const ID字間 = 1;
@@ -516,7 +535,7 @@ export function ShapeKanbanCardNode({ node, active }: ShapeProps): JSX.Element {
516
535
  const 下帯 = FOOTER下 - AVATAR中心Y + AVATAR半径 + AVATAR枠 / 2 + 帯間余白;
517
536
  const 中帯 = 名前字 * ASCENT + 名前字 * 説明基準比 + 名前字 * 説明字比 * DESCENT;
518
537
  const 自然高 = 上帯 + 中帯 + 下帯;
519
- const ID幅 = ID文.length * MONO_ADVANCE * ID字 + ID字間 * ID文.length;
538
+ const ID幅 = 等幅の桁(ID文) * MONO_ADVANCE * ID字 + ID字間 * [...ID文].length;
520
539
  const 自然幅 = 左右余白 + ID幅 + 上段隙間 + PILL幅 + 左右余白;
521
540
  // 札の中身だけを縦横同率で縮め、 入る札では 1 のまま従来の大きさを保つ。
522
541
  const 倍 = Math.min(1, w / 自然幅, h / 自然高);
@@ -594,7 +613,7 @@ export function ShapeKanbanCardNode({ node, active }: ShapeProps): JSX.Element {
594
613
  fill="#5a4a1a"
595
614
  fontFamily="ui-monospace, SFMono-Regular, Menlo, monospace"
596
615
  >
597
- IN PROG
616
+ {字引("kanbanCard.status")}
598
617
  </text>
599
618
  {/* タスク名 = title (中央上、 大きく太字) */}
600
619
  <text
@@ -616,7 +635,7 @@ export function ShapeKanbanCardNode({ node, active }: ShapeProps): JSX.Element {
616
635
  textAnchor="middle"
617
636
  fill="var(--cdl-text-dim, #5a6270)"
618
637
  >
619
- shape-driven kind 実装
638
+ {字引("kanbanCard.task")}
620
639
  </text>
621
640
  {/* footer = 2 avatars 左 + due date 右 */}
622
641
  <g transform={`translate(${x + 左右余白 * 倍}, ${footY})`}>
@@ -668,7 +687,7 @@ export function ShapeKanbanCardNode({ node, active }: ShapeProps): JSX.Element {
668
687
  textAnchor="end"
669
688
  fill="var(--cdl-text-dim, #5a6270)"
670
689
  >
671
- Jul 10
690
+ {字引("kanbanCard.due")}
672
691
  </text>
673
692
  </>
674
693
  )}
package/src/lang.ts ADDED
@@ -0,0 +1,168 @@
1
+ /**
2
+ * 描画側が書く固定の字の言語 (#847)。
3
+ *
4
+ * 書き手が何を渡しても描かれる字 (形の看板 / 絵の中の見本の値 / 行の名前) を 1 つの辞書に集め、
5
+ * 図の `lang` で引き替える。 書き手の字 (`title` / `subtitle` / 行 / 札) はこの辞書を通らない。
6
+ *
7
+ * **既定は英語**。 字を直接日本語へ書き換えると、英語で使う人の図まで変わり、戻す口も無い。
8
+ * 指定の無い図は変更前と同じ字で描く。
9
+ *
10
+ * **JSX を持たない module に置く**。 型の定義 (`types.ts`) と配置 (`layout.ts`) からも読むため、
11
+ * ここから React を引き込むと字の幅を測る検査の読込みが重くなる (`kinds/text-width.ts` の注記と同じ)。
12
+ *
13
+ * 言語は `CdlStage` → `CdlNodeView` → 形の部品へ引数 (`lang`) で届く。
14
+ */
15
+
16
+ /** 図の言語。 書かない図は `"en"` で描く */
17
+ export type CdlLang = "en" | "ja";
18
+
19
+ /**
20
+ * 記法から届いた値を言語に直す。 型の外の値 (記法の文字列の書き損じ等) は既定の `"en"` に落とす。
21
+ *
22
+ * 落とす先を英語にするのは、指定の無い図と同じ見え方になるため。 書き損じで一部の字だけ
23
+ * 日本語になる形は作らない。
24
+ */
25
+ export function resolveLang(value: unknown): CdlLang {
26
+ return value === "ja" ? "ja" : "en";
27
+ }
28
+
29
+ /**
30
+ * 英語の辞書。 鍵の一覧の出どころで、値は変更前に描いていた字そのもの。
31
+ *
32
+ * 鍵は `形.役割` の形にする。 同じ字でも役割が違えば別の鍵にする (訳が分かれうるため)。
33
+ */
34
+ const en = {
35
+ /** `shape-terminal` の 2 行目 (命令の出力) */
36
+ "terminal.output": "→ 12 chunks in 1.8s",
37
+ /** `shape-code-block` の札の file 名 */
38
+ "codeBlock.fileName": "utils.ts",
39
+ /** `shape-kanban-card` の課題の番号 */
40
+ "kanbanCard.id": "CAR-1111",
41
+ /** `shape-kanban-card` の状態の札 */
42
+ "kanbanCard.status": "IN PROG",
43
+ /** `shape-kanban-card` の題の下の説明 */
44
+ "kanbanCard.task": "shape-driven kind 実装",
45
+ /** `shape-kanban-card` の期日 */
46
+ "kanbanCard.due": "Jul 10",
47
+ /** `shape-smart-contract` の契約の本文の 1 行 */
48
+ "smartContract.call": "function execute()",
49
+ /** `shape-blockchain-block` の題を渡さない時の見出し */
50
+ "blockchainBlock.title": "Block #421,873",
51
+ /** `shape-blockchain-block` の 1 行目の名前 (塊を識別する値) */
52
+ "blockchainBlock.hash": "hash",
53
+ /** `shape-blockchain-block` の 2 行目の名前 (1 つ前の塊の識別値) */
54
+ "blockchainBlock.prev": "prev",
55
+ /** `shape-blockchain-block` の 3 行目の名前 (取引をまとめた木の根) */
56
+ "blockchainBlock.merkle": "merkle",
57
+ /** `shape-blockchain-block` の 4 行目の名前 (採掘で試した値) */
58
+ "blockchainBlock.nonce": "nonce",
59
+ /** `shape-blockchain-block` の 5 行目の名前 (取引の件数) */
60
+ "blockchainBlock.tx": "tx",
61
+ /** `shape-blockchain-block` の 5 行目の値 */
62
+ "blockchainBlock.txCount": "142 tx",
63
+ /** `shape-rpc-node` の稼働の札 */
64
+ "rpcNode.status": "RPC ONLINE",
65
+ /** `shape-trust-bank` の看板 */
66
+ "trustBank.sign": "TRUST",
67
+ /** `shape-payment-provider` の看板 */
68
+ "paymentProvider.sign": "PAY",
69
+ /** `shape-payment-provider` の状態の札 */
70
+ "paymentProvider.status": "PROCESSING",
71
+ /** `shape-brokerage` の株価の帯 */
72
+ "brokerage.ticker": "▲NKY 38,240 ▲AAPL 194",
73
+ /** `shape-brokerage` の看板 */
74
+ "brokerage.sign": "$ SECURITIES",
75
+ /** `shape-exchange` の看板 */
76
+ "exchange.sign": "EXCHANGE",
77
+ /** `shape-exchange` の看板の下の札 */
78
+ "exchange.sub": "clearing",
79
+ /** `shape-atm` の天井の看板 */
80
+ "atm.sign": "ATM CORNER",
81
+ /** `shape-atm` の画面の案内 */
82
+ "atm.screen": "SELECT AMOUNT",
83
+ /** `shape-atm` の決める鍵 */
84
+ "atm.ok": "OK",
85
+ /** `shape-atm` のカードを入れる口の札 */
86
+ "atm.cardSlot": "CARD SLOT",
87
+ /** `shape-storefront` の看板 */
88
+ "storefront.sign": "SHOP",
89
+ /** `shape-storefront` の扉の札 */
90
+ "storefront.open": "OPEN",
91
+ /** `shape-warehouse` の看板 */
92
+ "warehouse.sign": "WAREHOUSE",
93
+ /** `shape-api-gateway` の看板 */
94
+ "apiGateway.sign": "API GATEWAY",
95
+ /** `shape-api-gateway` の 3 行目の要求の種類 */
96
+ "apiGateway.delete": "DEL",
97
+ /** `shape-api-gateway` の 1 行目の宛先 */
98
+ "apiGateway.users": "/users",
99
+ /** `shape-api-gateway` の 2 行目の宛先 */
100
+ "apiGateway.orders": "/orders",
101
+ /** `shape-api-gateway` の 3 行目の宛先 */
102
+ "apiGateway.session": "/session",
103
+ } as const;
104
+
105
+ /** 辞書の鍵 */
106
+ export type CdlTextKey = keyof typeof en;
107
+
108
+ /**
109
+ * 日本語の辞書。 型を `en` の鍵と同じにし、訳の抜けと余分な鍵を型で落とす。
110
+ *
111
+ * 製品名と定着した略語 (`API` / `ATM` / `RPC`) と、要求の種類の綴り (`DELETE`) は英字のまま残す。
112
+ * 日本語の文でもこの綴りで書くため。
113
+ */
114
+ const ja: Record<CdlTextKey, string> = {
115
+ "terminal.output": "→ 1.8 秒で完了",
116
+ "codeBlock.fileName": "共通.ts",
117
+ "kanbanCard.id": "#1111",
118
+ "kanbanCard.status": "進行中",
119
+ "kanbanCard.task": "形を実装",
120
+ "kanbanCard.due": "7/10",
121
+ "smartContract.call": "自動で実行する",
122
+ "blockchainBlock.title": "ブロック #421,873",
123
+ "blockchainBlock.hash": "ハッシュ",
124
+ "blockchainBlock.prev": "直前",
125
+ "blockchainBlock.merkle": "木の根",
126
+ "blockchainBlock.nonce": "ノンス",
127
+ "blockchainBlock.tx": "取引",
128
+ "blockchainBlock.txCount": "142 件",
129
+ "rpcNode.status": "RPC 稼働中",
130
+ "trustBank.sign": "信託",
131
+ "paymentProvider.sign": "決済",
132
+ "paymentProvider.status": "処理中",
133
+ "brokerage.ticker": "▲日経 38,240 ▲東証 2,710",
134
+ "brokerage.sign": "¥ 証券",
135
+ "exchange.sign": "取引所",
136
+ "exchange.sub": "清算",
137
+ "atm.sign": "ATM コーナー",
138
+ "atm.screen": "金額を選択",
139
+ "atm.ok": "確定",
140
+ "atm.cardSlot": "カード挿入口",
141
+ "storefront.sign": "商店",
142
+ "storefront.open": "営業",
143
+ "warehouse.sign": "倉庫",
144
+ "apiGateway.sign": "API 入口",
145
+ "apiGateway.delete": "DELETE",
146
+ "apiGateway.users": "/利用者",
147
+ "apiGateway.orders": "/注文",
148
+ "apiGateway.session": "/接続",
149
+ };
150
+
151
+ /** 言語ごとの辞書。 検査が鍵の一致を実行時にも確かめるため公開する */
152
+ export const CDL_TEXT: Readonly<Record<CdlLang, Readonly<Record<CdlTextKey, string>>>> = { en, ja };
153
+
154
+ /** 言語と鍵から描く字を引く */
155
+ export function cdlText(lang: CdlLang, key: CdlTextKey): string {
156
+ return CDL_TEXT[lang][key];
157
+ }
158
+
159
+ /**
160
+ * 部品が受け取った言語で辞書を引く関数を返す。 言語を渡さない呼び方は英語で引く。
161
+ *
162
+ * **部品へは引数で渡す** (React の文脈で届けない)。 形の部品は検査から関数として直接呼ばれており
163
+ * (`描く({ node, active })`)、文脈を読む hook はその呼び方で使えない。
164
+ */
165
+ export function 字引きを作る(lang: CdlLang | undefined): (key: CdlTextKey) => string {
166
+ const 言語 = lang ?? "en";
167
+ return (key) => CDL_TEXT[言語][key];
168
+ }
package/src/layout.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { CdlDiagram, LaidDiagram } from "./types";
2
+ import { resolveLang } from "./lang";
2
3
  import {
3
4
  CLEARANCE_LABEL_LABEL,
4
5
  CLEARANCE_NODE_LABEL,
@@ -422,6 +423,9 @@ export function layout(diag: CdlDiagram): LaidDiagram {
422
423
  // **遮断はここだけに置く**。 描画は必ず組み立てを通り、組み立て API も `er` も
423
424
  // 素通しにしてある。 上流にも置くと、どの入力でも通らない分岐が残る
424
425
  ...(diag.palette ? { palette: diag.palette } : {}),
426
+ // 描画側の固定の字の言語 (#847)。 既定 (`en`) と型の外の値は持たせない = 配置の結果の形を
427
+ // 変更前と揃え、記法の書き損じで一部の字だけ日本語になる形を作らない
428
+ ...(resolveLang(diag.lang) === "ja" ? { lang: "ja" as const } : {}),
425
429
  // 種類も配色と同じく空の目印を出さない。 遮断をここに揃えると手書きの図にも効く
426
430
  ...(diag.type ? { type: diag.type } : {}),
427
431
  bboxes,
@@ -14,6 +14,7 @@ import {
14
14
  } from "../event-handler";
15
15
  import type { Signal, Computed } from "../reactive";
16
16
  import { effect } from "../reactive";
17
+ import { textWidth } from "../kinds/text-width";
17
18
 
18
19
  /**
19
20
  * interactive primitive (input widget / formula / scroll trigger / event handler) を SVG diagram に
@@ -3473,6 +3474,22 @@ function ProgressGroupReadout({
3473
3474
  );
3474
3475
  }
3475
3476
 
3477
+ /**
3478
+ * 点の真上に置く札の揃え方。
3479
+ *
3480
+ * 中央揃えのままだと、範囲の端の点 (x = 余白 8) では札の幅の半分が図の外へ出て切れる
3481
+ * (実測 = 時刻 0 の `認可要求` が `可要求` に見えた、 #845)。 はみ出す側だけ揃え位置で押し戻す。
3482
+ *
3483
+ * 札の幅は描く前に分からないので `textWidth` で見積もる。 見積りは実物以上に倒してあるため、
3484
+ * 収まる札を端に寄せることはあっても、はみ出す札を中央に残すことは無い。
3485
+ */
3486
+ function sequenceLabelAnchor(x: number, name: string, fontSize: number, viewW: number): "start" | "middle" | "end" {
3487
+ const half = textWidth(name, fontSize) / 2;
3488
+ if (x - half < 0) return "start";
3489
+ if (x + half > viewW) return "end";
3490
+ return "middle";
3491
+ }
3492
+
3476
3493
  function SequenceTimelineReadout({
3477
3494
  id,
3478
3495
  label,
@@ -3543,7 +3560,7 @@ function SequenceTimelineReadout({
3543
3560
  <circle cx={x} cy={trackY} r={5} fill={color} stroke="#fff" strokeWidth={1.5}>
3544
3561
  <title>{`${e.name}: t=${e.t}`}</title>
3545
3562
  </circle>
3546
- <text x={x} y={trackY - 12} textAnchor="middle" fontSize={10} fill="var(--v4-ink, #1a1f2a)">
3563
+ <text x={x} y={trackY - 12} textAnchor={sequenceLabelAnchor(x, e.name, 10, viewW)} fontSize={10} fill="var(--v4-ink, #1a1f2a)">
3547
3564
  {e.name}
3548
3565
  </text>
3549
3566
  <text x={x} y={trackY + 18} textAnchor="middle" fontSize={9} fill="var(--v4-mute, #64748b)">
@@ -87,6 +87,7 @@ import {
87
87
  ShapeCustomerServiceNode,
88
88
  } from "../kinds/shape-people";
89
89
  import type { LaidNode } from "../types";
90
+ import type { CdlLang } from "../lang";
90
91
  import { hasUnresolvedRef, interpolate, resolveNumericAttr } from "./utils";
91
92
 
92
93
  /**
@@ -115,9 +116,12 @@ export function CdlNodeView({
115
116
  currentPhaseId,
116
117
  chartLineScale,
117
118
  関係の受け口,
119
+ lang,
118
120
  }: {
119
121
  node: LaidNode;
120
122
  active: boolean;
123
+ /** 描画側の固定の字の言語 (#847)。 字を持つ形の部品へ渡す。 渡さない呼び方は英語で描く */
124
+ lang?: CdlLang;
121
125
  /**
122
126
  * この phase の `draw` に載っているか (cdl#512)。 true の間だけ、 対象の種別が
123
127
  * phase の進み (`progress`) に合わせて左の起点から描かれる。
@@ -353,11 +357,11 @@ export function CdlNodeView({
353
357
  case "shape-window":
354
358
  return <ShapeWindowNode node={resolvedNode} active={active} />;
355
359
  case "shape-terminal":
356
- return <ShapeTerminalNode node={resolvedNode} active={active} />;
360
+ return <ShapeTerminalNode node={resolvedNode} active={active} lang={lang} />;
357
361
  case "shape-code-block":
358
- return <ShapeCodeBlockNode node={resolvedNode} active={active} />;
362
+ return <ShapeCodeBlockNode node={resolvedNode} active={active} lang={lang} />;
359
363
  case "shape-kanban-card":
360
- return <ShapeKanbanCardNode node={resolvedNode} active={active} />;
364
+ return <ShapeKanbanCardNode node={resolvedNode} active={active} lang={lang} />;
361
365
  case "shape-message-bubble":
362
366
  return <ShapeMessageBubbleNode node={resolvedNode} active={active} />;
363
367
  case "shape-gear":
@@ -375,11 +379,11 @@ export function CdlNodeView({
375
379
  case "shape-satellite":
376
380
  return <ShapeSatelliteNode node={resolvedNode} active={active} />;
377
381
  case "shape-smart-contract":
378
- return <ShapeSmartContractNode node={resolvedNode} active={active} />;
382
+ return <ShapeSmartContractNode node={resolvedNode} active={active} lang={lang} />;
379
383
  case "shape-blockchain-block":
380
- return <ShapeBlockchainBlockNode node={resolvedNode} active={active} />;
384
+ return <ShapeBlockchainBlockNode node={resolvedNode} active={active} lang={lang} />;
381
385
  case "shape-rpc-node":
382
- return <ShapeRpcNodeNode node={resolvedNode} active={active} />;
386
+ return <ShapeRpcNodeNode node={resolvedNode} active={active} lang={lang} />;
383
387
  case "shape-wallet":
384
388
  return <ShapeWalletNode node={resolvedNode} active={active} />;
385
389
  case "shape-nft":
@@ -397,29 +401,29 @@ export function CdlNodeView({
397
401
  case "shape-bank":
398
402
  return <ShapeBankNode node={resolvedNode} active={active} />;
399
403
  case "shape-trust-bank":
400
- return <ShapeTrustBankNode node={resolvedNode} active={active} />;
404
+ return <ShapeTrustBankNode node={resolvedNode} active={active} lang={lang} />;
401
405
  case "shape-payment-provider":
402
- return <ShapePaymentProviderNode node={resolvedNode} active={active} />;
406
+ return <ShapePaymentProviderNode node={resolvedNode} active={active} lang={lang} />;
403
407
  case "shape-credit-card":
404
408
  return <ShapeCreditCardNode node={resolvedNode} active={active} />;
405
409
  case "shape-brokerage":
406
- return <ShapeBrokerageNode node={resolvedNode} active={active} />;
410
+ return <ShapeBrokerageNode node={resolvedNode} active={active} lang={lang} />;
407
411
  case "shape-exchange":
408
- return <ShapeExchangeNode node={resolvedNode} active={active} />;
412
+ return <ShapeExchangeNode node={resolvedNode} active={active} lang={lang} />;
409
413
  case "shape-atm":
410
- return <ShapeAtmNode node={resolvedNode} active={active} />;
414
+ return <ShapeAtmNode node={resolvedNode} active={active} lang={lang} />;
411
415
  case "shape-website":
412
416
  return <ShapeWebsiteNode node={resolvedNode} active={active} />;
413
417
  case "shape-storefront":
414
- return <ShapeStorefrontNode node={resolvedNode} active={active} />;
418
+ return <ShapeStorefrontNode node={resolvedNode} active={active} lang={lang} />;
415
419
  case "shape-warehouse":
416
- return <ShapeWarehouseNode node={resolvedNode} active={active} />;
420
+ return <ShapeWarehouseNode node={resolvedNode} active={active} lang={lang} />;
417
421
  case "shape-online-shop":
418
422
  return <ShapeOnlineShopNode node={resolvedNode} active={active} />;
419
423
  case "shape-cdn-edge":
420
424
  return <ShapeCdnEdgeNode node={resolvedNode} active={active} />;
421
425
  case "shape-api-gateway":
422
- return <ShapeApiGatewayNode node={resolvedNode} active={active} />;
426
+ return <ShapeApiGatewayNode node={resolvedNode} active={active} lang={lang} />;
423
427
  case "shape-auditor":
424
428
  return <ShapeAuditorNode node={resolvedNode} active={active} />;
425
429
  case "shape-regulator":
@@ -426,6 +426,8 @@ export function CdlStage({
426
426
  <CdlNodeView
427
427
  node={node}
428
428
  active={activeSet.has(node.id)}
429
+ // 描画側の固定の字の言語 (#847)。 `layout` が既定以外の時だけ `lang` を持たせる
430
+ lang={laid.lang}
429
431
  関係の受け口={箱の受け口}
430
432
  drawing={描く}
431
433
  progress={描く ? 描く進み : progress}
package/src/types.ts CHANGED
@@ -5,6 +5,7 @@ import type {
5
5
  SequenceBoardMessage,
6
6
  SequenceBoardMessageKind,
7
7
  } from "./kinds/sequence-board-metrics";
8
+ import type { CdlLang } from "./lang";
8
9
 
9
10
  export type {
10
11
  SequenceBoardActor,
@@ -2613,6 +2614,15 @@ export type CdlDiagram = {
2613
2614
  * 既定の図にも当たる。
2614
2615
  */
2615
2616
  palette?: string;
2617
+ /**
2618
+ * 描画側が書く固定の字を、どの言語で描くか (#847)。 書かない図は `"en"` で描く。
2619
+ *
2620
+ * 引き替わるのは描画側が持つ字 (形の看板 / 絵の中の見本の値 / 行の名前) だけで、書き手の字
2621
+ * (`title` / `subtitle` / 札) は変わらない。 字の一覧は `src/lang.ts` が持つ。
2622
+ *
2623
+ * 型の外の値は `layout` が既定に落とす (記法から文字列で届く場合の書き損じ)。
2624
+ */
2625
+ lang?: CdlLang;
2616
2626
  /**
2617
2627
  * この図がどの種類か (#645)。 書かない図は種類を持たない。
2618
2628
  *
@@ -2840,6 +2850,11 @@ export type LaidDiagram = {
2840
2850
  topic: string;
2841
2851
  /** どの配色で描くか (#1553)。 `CdlDiagram.palette` をそのまま持ち越す */
2842
2852
  palette?: string;
2853
+ /**
2854
+ * 描画側の固定の字の言語 (#847)。 `CdlDiagram.lang` を既定以外の時だけ持ち越す。
2855
+ * 無い時は `"en"` で描く。
2856
+ */
2857
+ lang?: CdlLang;
2843
2858
  /** どの種類の図か (#645)。 `CdlDiagram.type` をそのまま持ち越す */
2844
2859
  type?: string;
2845
2860
  viewBox: { x: number; y: number; w: number; h: number };