@things-factory/figure-ui 10.1.156 → 10.1.158

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.
Files changed (37) hide show
  1. package/client/modeller/clips.ts +43 -4
  2. package/client/modeller/data-interface.ts +110 -0
  3. package/client/modeller/editor.ts +16 -2
  4. package/client/modeller/figure-clips.ts +125 -12
  5. package/client/modeller/figure-data-panel.ts +308 -0
  6. package/client/modeller/figure-preview.ts +41 -4
  7. package/client/modeller/refusals.ts +46 -0
  8. package/dist-client/modeller/clips.d.ts +32 -2
  9. package/dist-client/modeller/clips.js +32 -4
  10. package/dist-client/modeller/clips.js.map +1 -1
  11. package/dist-client/modeller/data-interface.d.ts +42 -0
  12. package/dist-client/modeller/data-interface.js +95 -0
  13. package/dist-client/modeller/data-interface.js.map +1 -0
  14. package/dist-client/modeller/editor.d.ts +3 -0
  15. package/dist-client/modeller/editor.js +18 -0
  16. package/dist-client/modeller/editor.js.map +1 -1
  17. package/dist-client/modeller/figure-clips.d.ts +10 -0
  18. package/dist-client/modeller/figure-clips.js +125 -10
  19. package/dist-client/modeller/figure-clips.js.map +1 -1
  20. package/dist-client/modeller/figure-data-panel.d.ts +39 -0
  21. package/dist-client/modeller/figure-data-panel.js +317 -0
  22. package/dist-client/modeller/figure-data-panel.js.map +1 -0
  23. package/dist-client/modeller/figure-preview.d.ts +7 -0
  24. package/dist-client/modeller/figure-preview.js +49 -4
  25. package/dist-client/modeller/figure-preview.js.map +1 -1
  26. package/dist-client/modeller/refusals.d.ts +5 -0
  27. package/dist-client/modeller/refusals.js +58 -0
  28. package/dist-client/modeller/refusals.js.map +1 -1
  29. package/dist-client/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +6 -6
  31. package/test/data-motion.test.ts +137 -0
  32. package/translations/en.json +86 -2
  33. package/translations/es.json +86 -2
  34. package/translations/ja.json +86 -2
  35. package/translations/ko.json +86 -2
  36. package/translations/ms.json +86 -2
  37. package/translations/zh.json +86 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/figure-ui",
3
- "version": "10.1.156",
3
+ "version": "10.1.158",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "@things-factory:registry": "https://registry.npmjs.org"
@@ -27,8 +27,8 @@
27
27
  "clean": "npm run clean:server && npm run clean:client"
28
28
  },
29
29
  "dependencies": {
30
- "@hatiolab/figure-model": "^0.6.3",
31
- "@hatiolab/things-scene": "^10.1.83",
30
+ "@hatiolab/figure-model": "^0.6.5",
31
+ "@hatiolab/things-scene": "^10.1.85",
32
32
  "@material/web": "^2.0.0",
33
33
  "@operato/data-grist": "^10.0.0",
34
34
  "@operato/graphql": "^10.0.0",
@@ -41,10 +41,10 @@
41
41
  "@operato/slot-layout-editor": "^10.29.25",
42
42
  "@operato/styles": "^10.0.0",
43
43
  "@operato/utils": "^10.0.0",
44
- "@things-factory/ai-assistant": "^10.1.156",
45
- "@things-factory/figure-service": "^10.1.156",
44
+ "@things-factory/ai-assistant": "^10.1.157",
45
+ "@things-factory/figure-service": "^10.1.157",
46
46
  "@things-factory/figure-viewer": "^10.1.155",
47
47
  "three": "^0.185.1"
48
48
  },
49
- "gitHead": "641146444c8ac4e510854c40824457ffad5cf3f1"
49
+ "gitHead": "bbd9815106c199e136cfa30e33d9a27d821bf5a4"
50
50
  }
@@ -0,0 +1,137 @@
1
+ /*
2
+ * **데이터 속성 · 동작 그룹** (ADR-0051 §2.2 · §3.2, 2026-10-13).
3
+ *
4
+ * 데이터 탭과 클립 패널이 고를 수 있게 내놓는 것과, 그 초안을 figure-model 이 받는지를 로봇 개(robotDogFigure) 위에서 본다.
5
+ * 규칙은 커널의 것이다 -- 여기는 목록과 초안과 문구만 시험한다.
6
+ */
7
+ import { test } from 'node:test'
8
+ import assert from 'node:assert/strict'
9
+ import { readFileSync } from 'node:fs'
10
+ import { fileURLToPath } from 'node:url'
11
+
12
+ import { editFigurePart, quadrupedRobotFigure, robotDogFigure } from '@hatiolab/figure-model'
13
+ import type { FigureAsset, FigureDataProperty } from '@hatiolab/figure-model'
14
+
15
+ import { clipGroupsOf, freeGroupIdOf, groupEdit, undrivenOf } from '../client/modeller/clips.ts'
16
+ import { connectionDraftOf, dataSampleOf, dataTargetsOf, freeDataIdOf, propertyDraftOf, sampleValueOf } from '../client/modeller/data-interface.ts'
17
+ import { editRefusalOf } from '../client/modeller/refusals.ts'
18
+
19
+ const read = (path: string) => readFileSync(fileURLToPath(new URL(path, import.meta.url)), 'utf8')
20
+ const DOG = robotDogFigure().asset
21
+ const say = (key: string, params: Record<string, string>) => `${key} ${JSON.stringify(params)}`
22
+ const refusalOf = (f: () => unknown) => {
23
+ try {
24
+ f()
25
+ } catch (error) {
26
+ return editRefusalOf(error, say)
27
+ }
28
+ throw new Error('not refused')
29
+ }
30
+
31
+ test('새 트랙이 쓸 수 있는 조작값: 그 클립에 없고, 그룹 밖 클립이 쓰지 않는 것', () => {
32
+ const legs = ['fl', 'fr', 'hl', 'hr'].flatMap(l => [`${l}-thigh`, `${l}-knee`])
33
+ /* run 은 locomotion 이라 walk 가 쓰는 다리도 쓸 수 있다 -- 이미 쓰는 것만 빠진다. */
34
+ assert.deepEqual(undrivenOf(DOG, 'run').filter(s => legs.includes(s)), [])
35
+ assert.ok(undrivenOf(DOG, 'run').includes('head'))
36
+ /* idle 은 무릎을 쓰지 않으므로 무릎을 더할 수 있다. */
37
+ assert.deepEqual(undrivenOf(DOG, 'idle').filter(s => legs.includes(s)), [])
38
+ /* 그룹에 넣는 새 클립은 그룹이 쓰는 다리를, 그룹 없는 새 클립은 아무도 쓰지 않는 것만. */
39
+ assert.ok(undrivenOf(DOG, '', 'locomotion').includes('fl-knee'))
40
+ assert.deepEqual(undrivenOf(DOG).filter(s => legs.includes(s)), [])
41
+ assert.deepEqual(undrivenOf(DOG), ['fl-hip', 'fr-hip', 'hl-hip', 'hr-hip', 'head'])
42
+ /* 그룹 밖 클립이 쓰는 조작값은 그룹의 새 클립도 쓸 수 없다. */
43
+ const waving = editFigurePart(DOG, { kind: 'set-driver', state: 'fl-hip', clip: 'wave', duration: 1, keys: [{ seconds: 0, value: 0 }, { seconds: 1, value: 20 }] })
44
+ assert.equal(undrivenOf(waving, '', 'locomotion').includes('fl-hip'), false)
45
+ assert.equal(undrivenOf(waving, 'run').includes('fl-hip'), false)
46
+ /* 고른 대로 쓰면 커널이 받는다. */
47
+ editFigurePart(DOG, { kind: 'set-driver', state: 'fl-knee', clip: 'trot', duration: 1, keys: [{ seconds: 0, value: 0 }, { seconds: 1, value: 30 }], group: 'locomotion' })
48
+ })
49
+
50
+ test('그룹 목록과 빈 클립 · 그룹 id 는 사람이 적은 이름에서 만들지 않는다', () => {
51
+ const { groups, free } = clipGroupsOf(DOG)
52
+ assert.deepEqual(groups.map(g => [g.id, g.label, g.clips, g.defaultClip]), [['locomotion', '보행', ['idle', 'walk', 'run'], 'idle']])
53
+ assert.deepEqual(free, [])
54
+ assert.equal(freeGroupIdOf(DOG), 'motion')
55
+ const two = editFigurePart(DOG, groupEdit(groups[0]!, { transitionSeconds: 0.5, defaultClip: 'walk' }))
56
+ assert.deepEqual([two.motionGroups![0]!.transitionSeconds, two.motionGroups![0]!.defaultClip], [0.5, 'walk'])
57
+ })
58
+
59
+ test('속성 종류마다 고를 수 있는 대상과, 첫 연결 초안을 커널이 받는다', () => {
60
+ const kinds = (kind: FigureDataProperty['kind']) => [...new Set(dataTargetsOf(DOG, kind).map(t => t.target.kind))]
61
+ assert.deepEqual(kinds('number'), ['state', 'motion-speed'])
62
+ assert.deepEqual(kinds('enum'), ['state', 'palette', 'motion-clip'])
63
+ let asset: FigureAsset = DOG
64
+ const load: FigureDataProperty = propertyDraftOf(asset, 'load level', 'number')
65
+ assert.deepEqual([load.id, load.kind], ['load-level', 'number'])
66
+ asset = editFigurePart(asset, { kind: 'set-data-property', property: { ...load, unit: 'deg', min: -30, max: 30 } as FigureDataProperty })
67
+ const hip = dataTargetsOf(asset, 'number').find(t => t.target.kind === 'state' && t.target.input === 'fl-hip')!
68
+ const draft = connectionDraftOf(asset, asset.dataInterface!.properties.find(p => p.id === 'load-level')!, hip)
69
+ assert.deepEqual(draft.transform, { kind: 'identity' }, '같은 단위는 그대로')
70
+ const cadence = asset.dataInterface!.properties.find(p => p.id === 'cadence')!
71
+ assert.deepEqual(connectionDraftOf(asset, cadence, hip).transform, { kind: 'affine', scale: 1, offset: 0 }, '다른 단위는 배율로 -- 단위를 짐작하지 않는다')
72
+ asset = editFigurePart(asset, { kind: 'set-data-connection', connection: draft })
73
+ /* 선택값 하나를 색에: 값마다 지금 색으로 시작한다. */
74
+ const mode = asset.dataInterface!.properties.find(p => p.id === 'mode')!
75
+ const body = dataTargetsOf(asset, 'enum').find(t => t.target.kind === 'palette' && t.target.token === 'palette.body')!
76
+ const colour = connectionDraftOf(asset, mode, body)
77
+ assert.deepEqual(colour.transform, { kind: 'lookup', cases: ['idle', 'walk', 'run'].map(when => ({ when, value: DOG.palette['palette.body'] })) })
78
+ editFigurePart(asset, { kind: 'set-data-connection', connection: colour })
79
+ assert.equal(freeDataIdOf(['mode', 'mode-2'], 'mode'), 'mode-3')
80
+ assert.equal(freeDataIdOf([], '배터리'), 'data', '영문 id 가 남지 않으면 data')
81
+ })
82
+
83
+ test('샘플 값: 무엇을 움직이는지 커널의 평가로, 틀린 값 하나면 통째로 거절한다', () => {
84
+ const ok = dataSampleOf(DOG, { mode: 'run', cadence: 2, headAngle: 10, batteryLow: true })
85
+ assert.ok('result' in ok)
86
+ assert.deepEqual(JSON.parse(JSON.stringify(ok.result.motion)), { locomotion: { clip: 'run', speed: 2 } })
87
+ const bad = dataSampleOf(DOG, { mode: 'run', headAngle: 90 })
88
+ assert.ok('issues' in bad)
89
+ assert.deepEqual(bad.issues.map(i => `${i.property} ${i.reason}`), ['headAngle out-of-range'])
90
+ const mode = DOG.dataInterface!.properties.find(p => p.id === 'mode')!
91
+ const low = DOG.dataInterface!.properties.find(p => p.id === 'batteryLow')!
92
+ const cadence = DOG.dataInterface!.properties.find(p => p.id === 'cadence')!
93
+ assert.deepEqual([sampleValueOf(mode, 'walk'), sampleValueOf(low, 'false'), sampleValueOf(cadence, '0')], ['walk', false, 0])
94
+ assert.ok(Number.isNaN(sampleValueOf(cadence, '') as number), '빈 칸은 0 이 아니다')
95
+ })
96
+
97
+ test('데이터 · 동작 거절은 커널 문장이 아니라 reason 과 값으로 말한다', () => {
98
+ assert.match(refusalOf(() => editFigurePart(DOG, { kind: 'remove-data-property', id: 'mode' })), /^figure\.text\.data-property-connected .*mode-locomotion/)
99
+ assert.match(refusalOf(() => editFigurePart(DOG, { kind: 'remove-driver', state: 'fl-knee' })), /^figure\.text\.driver-clip-required .*walk/)
100
+ assert.match(
101
+ refusalOf(() => editFigurePart(DOG, { kind: 'set-data-connection', connection: { id: 'head-2', property: 'headAngle', transform: { kind: 'affine', scale: 2, offset: 0 }, target: { kind: 'state', input: 'head', sourceMode: 'observed' } } })),
102
+ /^figure\.text\.data-transform-range .*"min":"-90".*"low":"-45"/
103
+ )
104
+ const lone = editFigurePart(quadrupedRobotFigure().asset, { kind: 'set-driver', state: 'fl-knee', clip: 'walk', duration: 1, keys: [{ seconds: 0, value: 0 }, { seconds: 1, value: 10 }] })
105
+ assert.match(refusalOf(() => editFigurePart(lone, { kind: 'set-driver', state: 'fl-knee', clip: 'run', duration: 1, keys: [{ seconds: 0, value: 0 }, { seconds: 1, value: 10 }] })), /^figure\.text\.driver-writers /)
106
+ })
107
+
108
+ test('데이터 탭 · 클립 패널 · 미리보기의 배선과 문구', () => {
109
+ const editor = read('../client/modeller/editor.ts')
110
+ assert.match(editor, /renderTab\('data'/)
111
+ assert.match(editor, /<figure-data-panel/)
112
+ assert.match(editor, /\.sample=\$\{this\.dataSample\}/)
113
+ const preview = read('../client/modeller/figure-preview.ts')
114
+ /* 라이브 스냅숏은 저장 모델이 아니라 Figure 의 figureData 접근자로 간다. */
115
+ assert.match(preview, /\.figureData = value/)
116
+ assert.match(preview, /tellInstances\('motion'/)
117
+ const clips = read('../client/modeller/figure-clips.ts')
118
+ assert.match(clips, /kind: 'remove-driver', state: track\.state, clip: clip\.name/)
119
+ const keys = [read('../client/modeller/figure-data-panel.ts'), clips, read('../client/modeller/refusals.ts')].flatMap(src => [...src.matchAll(/'(figure\.[a-z.-]*[a-z])'/g)].map(m => m[1]!))
120
+ /* Keys made from a value: each value the kernel can give has its words. */
121
+ const made = [
122
+ /* The tab's short name, the one it shows under its icon (renderTab reads figure.label.tab-short-<tab>). */
123
+ 'figure.label.tab-short-data',
124
+ ...['number', 'boolean', 'enum'].map(k => `figure.label.data-kind-${k}`),
125
+ ...['state', 'palette', 'motion-clip', 'motion-speed'].flatMap(k => [`figure.label.data-target-${k}`, `figure.text.data-target-absent-${k}`]),
126
+ ...['observed', 'commanded'].map(k => `figure.label.data-${k}`),
127
+ ...['label', 'unit', 'min', 'default', 'values'].map(k => `figure.text.data-property-invalid-${k}`),
128
+ ...['kind', 'unit', 'range', 'cases', 'output'].map(k => `figure.text.data-transform-${k}`),
129
+ ...['group-unknown', 'clip-not-in-group', 'speed-out-of-range', 'group-selection-required'].map(k => `figure.text.motion-setting-${k}`),
130
+ ...['not-a-record', 'property-unknown', 'type-mismatch', 'out-of-range', 'enum-unknown'].map(k => `figure.text.data-value-${k}`)
131
+ ]
132
+ for (const lang of ['ko', 'en', 'ja', 'zh', 'ms', 'es']) {
133
+ const table = JSON.parse(read(`../translations/${lang}.json`))
134
+ for (const key of keys) assert.ok(table[key], `${lang}: ${key}`)
135
+ for (const key of made) assert.ok(table[key], `${lang}: ${key}`)
136
+ }
137
+ })
@@ -504,7 +504,7 @@
504
504
  "figure.text.view-settings-are-not-saved": "these settings are not saved.",
505
505
  "figure.text.what-a-figure-is": "a Figure is a 3D component you author. compose it from simple shapes and it becomes a component you can place on a drawing, sharing shape and material across every instance.",
506
506
  "figure.text.what-changed-in-this-release": "what changed in this release",
507
- "figure.title.figures": "figures",
507
+ "figure.title.figures": "figure catalog",
508
508
  "figure.title.new-figure": "new figure",
509
509
  "figure.title.save-new-figure": "save figure draft",
510
510
  "figure.text.no-parts-yet": "No parts yet. Pick a shape above to place one.",
@@ -1024,5 +1024,89 @@
1024
1024
  "figure.label.slot-preset-param-rows": "rows",
1025
1025
  "figure.label.slot-preset-param-cols": "columns",
1026
1026
  "figure.label.slot-preset-param-stackLimit": "stack limit",
1027
- "figure.label.placement": "fastening"
1027
+ "figure.label.placement": "fastening",
1028
+ "figure.label.motion-groups": "motion groups",
1029
+ "figure.label.motion-group": "motion group",
1030
+ "figure.label.no-motion-group": "no group",
1031
+ "figure.label.motion-group-name": "name",
1032
+ "figure.label.motion-group-default": "default clip",
1033
+ "figure.label.motion-group-transition": "transition (s)",
1034
+ "figure.button.add-motion-group": "make a group",
1035
+ "figure.button.remove-motion-group": "remove the group",
1036
+ "figure.button.remove-clip-from-group": "take out of the group",
1037
+ "figure.button.add-clip-to-group": "add a clip",
1038
+ "figure.text.motion-groups-explained": "Clips of one group play one at a time. For several clips to move the same control, put them in one group. A change of clip blends from the pose left to the new clip over the transition time.",
1039
+ "figure.label.tab-data": "data",
1040
+ "figure.label.data-properties": "data properties",
1041
+ "figure.text.data-properties-explained": "Values the site data gives this figure. A board maps its data onto these ids, never onto a part or an inner name.",
1042
+ "figure.label.data-connections": "connections",
1043
+ "figure.text.data-connections-explained": "What a data property moves: a control, a colour, or a motion group's clip and speed. Every value the property can take must land inside what the target takes.",
1044
+ "figure.text.data-no-properties": "Add a data property first.",
1045
+ "figure.label.data-kind": "kind",
1046
+ "figure.label.data-kind-number": "number",
1047
+ "figure.label.data-kind-boolean": "true/false",
1048
+ "figure.label.data-kind-enum": "choice",
1049
+ "figure.label.data-id": "id",
1050
+ "figure.label.data-unit": "unit",
1051
+ "figure.label.data-min": "min",
1052
+ "figure.label.data-max": "max",
1053
+ "figure.label.data-default": "default",
1054
+ "figure.label.data-values": "values (comma-separated)",
1055
+ "figure.label.data-true": "true",
1056
+ "figure.label.data-false": "false",
1057
+ "figure.button.add-data-property": "add a data property",
1058
+ "figure.button.remove-data-property": "remove the data property",
1059
+ "figure.label.data-property": "data property",
1060
+ "figure.label.data-target": "target",
1061
+ "figure.label.data-target-state": "control {name}",
1062
+ "figure.label.data-target-palette": "colour {name}",
1063
+ "figure.label.data-target-motion-clip": "{name} clip",
1064
+ "figure.label.data-target-motion-speed": "{name} speed",
1065
+ "figure.button.add-data-connection": "add a connection",
1066
+ "figure.button.remove-data-connection": "remove the connection",
1067
+ "figure.label.data-source-mode": "applies",
1068
+ "figure.label.data-observed": "measured (at once)",
1069
+ "figure.label.data-commanded": "commanded (travels at the sweep)",
1070
+ "figure.label.data-transform": "transform",
1071
+ "figure.label.data-identity": "as it is",
1072
+ "figure.label.data-affine": "scale and offset",
1073
+ "figure.label.data-scale": "scale",
1074
+ "figure.label.data-offset": "offset",
1075
+ "figure.label.data-sample": "sample data",
1076
+ "figure.text.data-sample-explained": "Try values to see what they move. Not saved. Applied to the preview, the preview's figure runs on them.",
1077
+ "figure.label.data-sample-in-preview": "apply to the preview",
1078
+ "figure.text.data-value-not-a-record": "The sample is not a set of values.",
1079
+ "figure.text.data-value-property-unknown": "{property} is not a data property of this figure.",
1080
+ "figure.text.data-value-type-mismatch": "The value of {property} is of the wrong kind.",
1081
+ "figure.text.data-value-out-of-range": "The value of {property} is outside its range.",
1082
+ "figure.text.data-value-enum-unknown": "{property} does not take this value.",
1083
+ "figure.text.data-id-invalid": "The id {id} cannot be used: a letter first, then letters, digits, - and _.",
1084
+ "figure.text.data-id-duplicate": "The id {id} is already used.",
1085
+ "figure.text.data-property-invalid-label": "Give {property} a name.",
1086
+ "figure.text.data-property-invalid-unit": "The unit of {property} is not a unit.",
1087
+ "figure.text.data-property-invalid-min": "The range of {property} runs from a smaller number to a larger one.",
1088
+ "figure.text.data-property-invalid-default": "The default of {property} must be within its range or its values.",
1089
+ "figure.text.data-property-invalid-values": "The values of {property} are one or more, each once.",
1090
+ "figure.text.data-property-absent": "{property} is not a data property of this figure.",
1091
+ "figure.text.data-target-absent-state": "There is no control {id}. Remove the connection that writes it first.",
1092
+ "figure.text.data-target-absent-palette": "There is no colour {id}.",
1093
+ "figure.text.data-target-absent-motion-clip": "There is no motion group {id}.",
1094
+ "figure.text.data-target-absent-motion-speed": "There is no motion group {id}.",
1095
+ "figure.text.data-target-duplicate": "Connections {a} and {b} both write {id}; one connection per target.",
1096
+ "figure.text.data-transform-kind": "This transform does not fit this property and target: a number goes as it is or scaled, a choice or true/false case by case.",
1097
+ "figure.text.data-transform-unit": "The units differ ({from} → {to}). Say how one becomes the other with a scale and offset.",
1098
+ "figure.text.data-transform-range": "It gives {min} to {max}; the target takes {low} to {high}.",
1099
+ "figure.text.data-transform-cases": "Give each value once: {value} is missing, given twice, or not a value of the property.",
1100
+ "figure.text.data-transform-output": "{value} is not a value the target takes.",
1101
+ "figure.text.data-property-connected": "Remove the connections that read {property} first: {connections}.",
1102
+ "figure.text.motion-clip-absent": "{clip} is not a clip {group} can hold.",
1103
+ "figure.text.motion-clip-shared": "{clip} is already in {group}; a clip is in one group.",
1104
+ "figure.text.driver-writers": "Clips {clips} all move {state}. Clips that move one control go in one motion group.",
1105
+ "figure.text.driver-clip-required": "Several clips ({clips}) move {state}; choose whose track.",
1106
+ "figure.text.motion-setting-group-unknown": "There is no motion group {group}.",
1107
+ "figure.text.motion-setting-clip-not-in-group": "{clip} is not a clip of {group}.",
1108
+ "figure.text.motion-setting-speed-out-of-range": "The speed of {group} is 0 to 3.",
1109
+ "figure.text.motion-setting-group-selection-required": "Choose which clip of {group} plays.",
1110
+ "figure.text.motion-key-outside-range": "The key {value} of {input} is outside {min} to {max}; a grouped clip keys inside the range.",
1111
+ "figure.label.tab-short-data": "Data"
1028
1112
  }
@@ -504,7 +504,7 @@
504
504
  "figure.text.view-settings-are-not-saved": "esta configuración no se guarda.",
505
505
  "figure.text.what-a-figure-is": "una Figure es un componente 3D que usted crea. compóngala a partir de formas sencillas y se convierte en un componente que puede colocar en un plano, compartiendo forma y material entre todas las instancias.",
506
506
  "figure.text.what-changed-in-this-release": "qué cambió en esta publicación",
507
- "figure.title.figures": "figuras",
507
+ "figure.title.figures": "catálogo de figuras",
508
508
  "figure.title.new-figure": "nueva figura",
509
509
  "figure.title.save-new-figure": "guardar borrador de figura",
510
510
  "figure.text.no-parts-yet": "Aún no hay piezas. Elija una forma arriba para colocar una.",
@@ -1024,5 +1024,89 @@
1024
1024
  "figure.label.slot-preset-param-rows": "filas",
1025
1025
  "figure.label.slot-preset-param-cols": "columnas",
1026
1026
  "figure.label.slot-preset-param-stackLimit": "límite de apilado",
1027
- "figure.label.placement": "sujeción"
1027
+ "figure.label.placement": "sujeción",
1028
+ "figure.label.motion-groups": "grupos de movimiento",
1029
+ "figure.label.motion-group": "grupo de movimiento",
1030
+ "figure.label.no-motion-group": "sin grupo",
1031
+ "figure.label.motion-group-name": "nombre",
1032
+ "figure.label.motion-group-default": "clip predeterminado",
1033
+ "figure.label.motion-group-transition": "transición (s)",
1034
+ "figure.button.add-motion-group": "crear grupo",
1035
+ "figure.button.remove-motion-group": "quitar el grupo",
1036
+ "figure.button.remove-clip-from-group": "sacar del grupo",
1037
+ "figure.button.add-clip-to-group": "añadir clip",
1038
+ "figure.text.motion-groups-explained": "Los clips de un grupo se reproducen de uno en uno. Para que varios clips muevan el mismo control, póngalos en un grupo. Al cambiar de clip, se pasa de la postura anterior al nuevo clip durante el tiempo de transición.",
1039
+ "figure.label.tab-data": "datos",
1040
+ "figure.label.data-properties": "propiedades de datos",
1041
+ "figure.text.data-properties-explained": "Valores que los datos de planta dan a esta figura. Un tablero asigna sus datos a estos id, nunca a una pieza ni a un nombre interno.",
1042
+ "figure.label.data-connections": "conexiones",
1043
+ "figure.text.data-connections-explained": "Lo que mueve una propiedad de datos: un control, un color, o el clip y la velocidad de un grupo de movimiento. Cada valor de la propiedad debe caber en lo que admite el destino.",
1044
+ "figure.text.data-no-properties": "Añada primero una propiedad de datos.",
1045
+ "figure.label.data-kind": "tipo",
1046
+ "figure.label.data-kind-number": "número",
1047
+ "figure.label.data-kind-boolean": "verdadero/falso",
1048
+ "figure.label.data-kind-enum": "opción",
1049
+ "figure.label.data-id": "id",
1050
+ "figure.label.data-unit": "unidad",
1051
+ "figure.label.data-min": "mín.",
1052
+ "figure.label.data-max": "máx.",
1053
+ "figure.label.data-default": "predeterminado",
1054
+ "figure.label.data-values": "valores (separados por comas)",
1055
+ "figure.label.data-true": "verdadero",
1056
+ "figure.label.data-false": "falso",
1057
+ "figure.button.add-data-property": "añadir propiedad de datos",
1058
+ "figure.button.remove-data-property": "quitar la propiedad de datos",
1059
+ "figure.label.data-property": "propiedad de datos",
1060
+ "figure.label.data-target": "destino",
1061
+ "figure.label.data-target-state": "control {name}",
1062
+ "figure.label.data-target-palette": "color {name}",
1063
+ "figure.label.data-target-motion-clip": "clip de {name}",
1064
+ "figure.label.data-target-motion-speed": "velocidad de {name}",
1065
+ "figure.button.add-data-connection": "añadir conexión",
1066
+ "figure.button.remove-data-connection": "quitar la conexión",
1067
+ "figure.label.data-source-mode": "aplicación",
1068
+ "figure.label.data-observed": "medido (al instante)",
1069
+ "figure.label.data-commanded": "ordenado (se desplaza con el tiempo)",
1070
+ "figure.label.data-transform": "transformación",
1071
+ "figure.label.data-identity": "tal cual",
1072
+ "figure.label.data-affine": "escala y desplazamiento",
1073
+ "figure.label.data-scale": "escala",
1074
+ "figure.label.data-offset": "desplazamiento",
1075
+ "figure.label.data-sample": "datos de ejemplo",
1076
+ "figure.text.data-sample-explained": "Pruebe valores para ver qué mueven. No se guardan. Aplicados a la vista previa, la figura de la vista previa funciona con ellos.",
1077
+ "figure.label.data-sample-in-preview": "aplicar a la vista previa",
1078
+ "figure.text.data-value-not-a-record": "El ejemplo no es un conjunto de valores.",
1079
+ "figure.text.data-value-property-unknown": "{property} no es una propiedad de datos de esta figura.",
1080
+ "figure.text.data-value-type-mismatch": "El valor de {property} es del tipo equivocado.",
1081
+ "figure.text.data-value-out-of-range": "El valor de {property} está fuera de su rango.",
1082
+ "figure.text.data-value-enum-unknown": "{property} no admite este valor.",
1083
+ "figure.text.data-id-invalid": "El id {id} no sirve: una letra primero, luego letras, dígitos, - y _.",
1084
+ "figure.text.data-id-duplicate": "El id {id} ya existe.",
1085
+ "figure.text.data-property-invalid-label": "Dé un nombre a {property}.",
1086
+ "figure.text.data-property-invalid-unit": "La unidad de {property} no es válida.",
1087
+ "figure.text.data-property-invalid-min": "El rango de {property} va de un número menor a uno mayor.",
1088
+ "figure.text.data-property-invalid-default": "El valor predeterminado de {property} debe estar en su rango o sus valores.",
1089
+ "figure.text.data-property-invalid-values": "Los valores de {property} son uno o más, cada uno una vez.",
1090
+ "figure.text.data-property-absent": "{property} no es una propiedad de datos de esta figura.",
1091
+ "figure.text.data-target-absent-state": "No existe el control {id}. Quite primero la conexión que lo escribe.",
1092
+ "figure.text.data-target-absent-palette": "No existe el color {id}.",
1093
+ "figure.text.data-target-absent-motion-clip": "No existe el grupo de movimiento {id}.",
1094
+ "figure.text.data-target-absent-motion-speed": "No existe el grupo de movimiento {id}.",
1095
+ "figure.text.data-target-duplicate": "Las conexiones {a} y {b} escriben ambas {id}; una conexión por destino.",
1096
+ "figure.text.data-transform-kind": "Esta transformación no encaja: un número tal cual o escalado; una opción o verdadero/falso caso por caso.",
1097
+ "figure.text.data-transform-unit": "Las unidades difieren ({from} → {to}). Indique la conversión con escala y desplazamiento.",
1098
+ "figure.text.data-transform-range": "Da {min} a {max}; el destino admite {low} a {high}.",
1099
+ "figure.text.data-transform-cases": "Dé cada valor una vez: {value} falta, está repetido o no es un valor de la propiedad.",
1100
+ "figure.text.data-transform-output": "{value} no es un valor que admita el destino.",
1101
+ "figure.text.data-property-connected": "Quite primero las conexiones que leen {property}: {connections}.",
1102
+ "figure.text.motion-clip-absent": "{clip} no es un clip que pueda contener {group}.",
1103
+ "figure.text.motion-clip-shared": "{clip} ya está en {group}; un clip está en un solo grupo.",
1104
+ "figure.text.driver-writers": "Los clips {clips} mueven todos {state}. Los clips que mueven un control van en un grupo de movimiento.",
1105
+ "figure.text.driver-clip-required": "Varios clips ({clips}) mueven {state}; elija la pista de cuál.",
1106
+ "figure.text.motion-setting-group-unknown": "No existe el grupo de movimiento {group}.",
1107
+ "figure.text.motion-setting-clip-not-in-group": "{clip} no es un clip de {group}.",
1108
+ "figure.text.motion-setting-speed-out-of-range": "La velocidad de {group} es de 0 a 3.",
1109
+ "figure.text.motion-setting-group-selection-required": "Elija qué clip de {group} se reproduce.",
1110
+ "figure.text.motion-key-outside-range": "La clave {value} de {input} está fuera de {min} a {max}; un clip de grupo usa claves dentro del rango.",
1111
+ "figure.label.tab-short-data": "Datos"
1028
1112
  }
@@ -504,7 +504,7 @@
504
504
  "figure.text.view-settings-are-not-saved": "この設定は保存されません。",
505
505
  "figure.text.what-a-figure-is": "a Figure is a 3D component you author. Compose it from simple shapes and it becomes a component you can place on a drawing, sharing shape and material across every instance.",
506
506
  "figure.text.what-changed-in-this-release": "このバージョンで変わったこと",
507
- "figure.title.figures": "figures",
507
+ "figure.title.figures": "Figure カタログ",
508
508
  "figure.title.new-figure": "new figure",
509
509
  "figure.title.save-new-figure": "Figure の下書きを保存",
510
510
  "figure.text.no-parts-yet": "No parts yet. Pick a shape above to place one.",
@@ -1024,5 +1024,89 @@
1024
1024
  "figure.label.slot-preset-param-rows": "行",
1025
1025
  "figure.label.slot-preset-param-cols": "列",
1026
1026
  "figure.label.slot-preset-param-stackLimit": "段積み数",
1027
- "figure.label.placement": "取り付け"
1027
+ "figure.label.placement": "取り付け",
1028
+ "figure.label.motion-groups": "モーショングループ",
1029
+ "figure.label.motion-group": "モーショングループ",
1030
+ "figure.label.no-motion-group": "グループなし",
1031
+ "figure.label.motion-group-name": "名前",
1032
+ "figure.label.motion-group-default": "既定クリップ",
1033
+ "figure.label.motion-group-transition": "切替時間 (秒)",
1034
+ "figure.button.add-motion-group": "グループを作成",
1035
+ "figure.button.remove-motion-group": "グループを削除",
1036
+ "figure.button.remove-clip-from-group": "グループから外す",
1037
+ "figure.button.add-clip-to-group": "クリップを追加",
1038
+ "figure.text.motion-groups-explained": "同じグループのクリップは一度に一つだけ再生します。同じ操作値を複数のクリップで動かすには、それらを一つのグループに入れます。クリップを切り替えると、切替時間のあいだ直前の姿勢から新しいクリップへつながります。",
1039
+ "figure.label.tab-data": "データ",
1040
+ "figure.label.data-properties": "データ属性",
1041
+ "figure.text.data-properties-explained": "現場データがこのFigureに与える値です。ボードはデータをこのIDに対応させます。部品や内部の名前は使いません。",
1042
+ "figure.label.data-connections": "内部接続",
1043
+ "figure.text.data-connections-explained": "データ属性が動かす対象です。操作値、色、モーショングループのクリップと速度を選びます。属性のすべての値が対象の範囲に入る必要があります。",
1044
+ "figure.text.data-no-properties": "先にデータ属性を追加します。",
1045
+ "figure.label.data-kind": "種類",
1046
+ "figure.label.data-kind-number": "数値",
1047
+ "figure.label.data-kind-boolean": "真偽",
1048
+ "figure.label.data-kind-enum": "選択値",
1049
+ "figure.label.data-id": "ID",
1050
+ "figure.label.data-unit": "単位",
1051
+ "figure.label.data-min": "最小",
1052
+ "figure.label.data-max": "最大",
1053
+ "figure.label.data-default": "既定値",
1054
+ "figure.label.data-values": "値の一覧 (カンマ区切り)",
1055
+ "figure.label.data-true": "真",
1056
+ "figure.label.data-false": "偽",
1057
+ "figure.button.add-data-property": "データ属性を追加",
1058
+ "figure.button.remove-data-property": "データ属性を削除",
1059
+ "figure.label.data-property": "データ属性",
1060
+ "figure.label.data-target": "対象",
1061
+ "figure.label.data-target-state": "操作値 {name}",
1062
+ "figure.label.data-target-palette": "色 {name}",
1063
+ "figure.label.data-target-motion-clip": "{name} クリップ",
1064
+ "figure.label.data-target-motion-speed": "{name} 速度",
1065
+ "figure.button.add-data-connection": "接続を追加",
1066
+ "figure.button.remove-data-connection": "接続を削除",
1067
+ "figure.label.data-source-mode": "適用",
1068
+ "figure.label.data-observed": "計測値 (即時)",
1069
+ "figure.label.data-commanded": "指令値 (遷移時間で移動)",
1070
+ "figure.label.data-transform": "変換",
1071
+ "figure.label.data-identity": "そのまま",
1072
+ "figure.label.data-affine": "倍率と加算",
1073
+ "figure.label.data-scale": "倍率",
1074
+ "figure.label.data-offset": "加算",
1075
+ "figure.label.data-sample": "サンプルデータ",
1076
+ "figure.text.data-sample-explained": "値を入れて何が動くかを確認します。保存しません。プレビューに適用すると、プレビューのFigureがこの値で動きます。",
1077
+ "figure.label.data-sample-in-preview": "プレビューに適用",
1078
+ "figure.text.data-value-not-a-record": "サンプル値の形式が正しくありません。",
1079
+ "figure.text.data-value-property-unknown": "{property} はこのFigureのデータ属性ではありません。",
1080
+ "figure.text.data-value-type-mismatch": "{property} の値の種類が合いません。",
1081
+ "figure.text.data-value-out-of-range": "{property} の値が範囲外です。",
1082
+ "figure.text.data-value-enum-unknown": "{property} にない値です。",
1083
+ "figure.text.data-id-invalid": "ID {id} は使えません。英字で始め、英字・数字・-・_ だけを使います。",
1084
+ "figure.text.data-id-duplicate": "ID {id} は既にあります。",
1085
+ "figure.text.data-property-invalid-label": "{property} の名前を入力します。",
1086
+ "figure.text.data-property-invalid-unit": "{property} の単位が正しくありません。",
1087
+ "figure.text.data-property-invalid-min": "{property} の範囲は小さい数から大きい数へ書きます。",
1088
+ "figure.text.data-property-invalid-default": "{property} の既定値は範囲または値の一覧の中にある必要があります。",
1089
+ "figure.text.data-property-invalid-values": "{property} の値の一覧は一つ以上で、重複しないようにします。",
1090
+ "figure.text.data-property-absent": "{property} はこのFigureのデータ属性ではありません。",
1091
+ "figure.text.data-target-absent-state": "操作値 {id} がありません。先にこの操作値を使う接続を削除します。",
1092
+ "figure.text.data-target-absent-palette": "色 {id} がありません。",
1093
+ "figure.text.data-target-absent-motion-clip": "モーショングループ {id} がありません。",
1094
+ "figure.text.data-target-absent-motion-speed": "モーショングループ {id} がありません。",
1095
+ "figure.text.data-target-duplicate": "接続 {a} と {b} が同じ対象 {id} に書き込みます。対象一つに接続は一つです。",
1096
+ "figure.text.data-transform-kind": "この変換はこの属性と対象に使えません。数値はそのままか倍率で、選択値と真偽は値ごとに決めます。",
1097
+ "figure.text.data-transform-unit": "単位が違います ({from} → {to})。倍率と加算で換算方法を書きます。",
1098
+ "figure.text.data-transform-range": "結果は {min} ~ {max} になります。対象は {low} ~ {high} だけを受け取ります。",
1099
+ "figure.text.data-transform-cases": "値ごとに一度ずつ決めます。{value} が抜けているか、重複しているか、属性にない値です。",
1100
+ "figure.text.data-transform-output": "{value} は対象が受け取る値ではありません。",
1101
+ "figure.text.data-property-connected": "{property} を読む接続 {connections} を先に削除します。",
1102
+ "figure.text.motion-clip-absent": "{clip} は {group} に入れられるクリップではありません。",
1103
+ "figure.text.motion-clip-shared": "{clip} は既に {group} にあります。クリップは一つのグループにだけ入れます。",
1104
+ "figure.text.driver-writers": "クリップ {clips} がすべて {state} を動かします。同じ操作値を動かすクリップは一つのモーショングループに入れます。",
1105
+ "figure.text.driver-clip-required": "{state} を複数のクリップ ({clips}) が動かします。どのクリップのトラックかを選びます。",
1106
+ "figure.text.motion-setting-group-unknown": "モーショングループ {group} がありません。",
1107
+ "figure.text.motion-setting-clip-not-in-group": "{clip} は {group} のクリップではありません。",
1108
+ "figure.text.motion-setting-speed-out-of-range": "{group} の速度は 0 ~ 3 です。",
1109
+ "figure.text.motion-setting-group-selection-required": "{group} で再生するクリップを選びます。",
1110
+ "figure.text.motion-key-outside-range": "{input} のキー {value} が範囲 {min} ~ {max} の外です。グループのクリップは範囲内のキーだけを使います。",
1111
+ "figure.label.tab-short-data": "データ"
1028
1112
  }
@@ -504,7 +504,7 @@
504
504
  "figure.text.view-settings-are-not-saved": "이 설정은 저장되지 않습니다.",
505
505
  "figure.text.what-a-figure-is": "Figure 는 직접 저작한 3D 컴포넌트입니다. 단순한 도형을 조합해 만들면 도면에 배치할 수 있는 컴포넌트가 되고, 여러 개를 배치해도 형상과 재질을 공유합니다.",
506
506
  "figure.text.what-changed-in-this-release": "이번 버전에서 바뀐 것",
507
- "figure.title.figures": "Figure",
507
+ "figure.title.figures": "Figure 카탈로그",
508
508
  "figure.title.new-figure": "새 Figure",
509
509
  "figure.title.save-new-figure": "Figure 초안 저장",
510
510
  "figure.text.no-parts-yet": "아직 부품이 없습니다. 위에서 도형을 골라 놓으세요.",
@@ -1024,5 +1024,89 @@
1024
1024
  "figure.label.slot-preset-param-rows": "행",
1025
1025
  "figure.label.slot-preset-param-cols": "열",
1026
1026
  "figure.label.slot-preset-param-stackLimit": "쌓는 단 수",
1027
- "figure.label.placement": "붙이기"
1027
+ "figure.label.placement": "붙이기",
1028
+ "figure.label.motion-groups": "동작 그룹",
1029
+ "figure.label.motion-group": "동작 그룹",
1030
+ "figure.label.no-motion-group": "그룹 없음",
1031
+ "figure.label.motion-group-name": "이름",
1032
+ "figure.label.motion-group-default": "기본 클립",
1033
+ "figure.label.motion-group-transition": "전환 시간 (초)",
1034
+ "figure.button.add-motion-group": "그룹 만들기",
1035
+ "figure.button.remove-motion-group": "그룹 삭제",
1036
+ "figure.button.remove-clip-from-group": "그룹에서 빼기",
1037
+ "figure.button.add-clip-to-group": "클립 넣기",
1038
+ "figure.text.motion-groups-explained": "한 그룹의 클립은 한 번에 하나만 재생합니다. 같은 조작값을 여러 클립이 움직이려면 그 클립들을 한 그룹에 넣습니다. 클립을 바꾸면 전환 시간 동안 이전 자세에서 새 클립으로 이어집니다.",
1039
+ "figure.label.tab-data": "데이터",
1040
+ "figure.label.data-properties": "데이터 속성",
1041
+ "figure.text.data-properties-explained": "현장 데이터가 이 Figure에 주는 값입니다. 보드는 데이터를 이 ID에 대응합니다. 부품이나 내부 이름은 쓰지 않습니다.",
1042
+ "figure.label.data-connections": "내부 연결",
1043
+ "figure.text.data-connections-explained": "데이터 속성이 움직이는 대상입니다. 조작값, 색, 동작 그룹의 클립과 배속을 고릅니다. 속성의 모든 값이 대상 범위 안에 들어야 합니다.",
1044
+ "figure.text.data-no-properties": "데이터 속성을 먼저 추가합니다.",
1045
+ "figure.label.data-kind": "종류",
1046
+ "figure.label.data-kind-number": "숫자",
1047
+ "figure.label.data-kind-boolean": "참/거짓",
1048
+ "figure.label.data-kind-enum": "선택값",
1049
+ "figure.label.data-id": "ID",
1050
+ "figure.label.data-unit": "단위",
1051
+ "figure.label.data-min": "최소",
1052
+ "figure.label.data-max": "최대",
1053
+ "figure.label.data-default": "기본값",
1054
+ "figure.label.data-values": "값 목록 (쉼표로 구분)",
1055
+ "figure.label.data-true": "참",
1056
+ "figure.label.data-false": "거짓",
1057
+ "figure.button.add-data-property": "데이터 속성 추가",
1058
+ "figure.button.remove-data-property": "데이터 속성 삭제",
1059
+ "figure.label.data-property": "데이터 속성",
1060
+ "figure.label.data-target": "대상",
1061
+ "figure.label.data-target-state": "조작값 {name}",
1062
+ "figure.label.data-target-palette": "색 {name}",
1063
+ "figure.label.data-target-motion-clip": "{name} 클립",
1064
+ "figure.label.data-target-motion-speed": "{name} 배속",
1065
+ "figure.button.add-data-connection": "연결 추가",
1066
+ "figure.button.remove-data-connection": "연결 삭제",
1067
+ "figure.label.data-source-mode": "적용",
1068
+ "figure.label.data-observed": "측정값 (바로 적용)",
1069
+ "figure.label.data-commanded": "명령값 (전이 시간으로 이동)",
1070
+ "figure.label.data-transform": "변환",
1071
+ "figure.label.data-identity": "그대로",
1072
+ "figure.label.data-affine": "배율과 더하기",
1073
+ "figure.label.data-scale": "배율",
1074
+ "figure.label.data-offset": "더하기",
1075
+ "figure.label.data-sample": "샘플 데이터",
1076
+ "figure.text.data-sample-explained": "값을 넣어 무엇이 움직이는지 확인합니다. 저장하지 않습니다. 미리보기에 적용하면 미리보기의 Figure가 이 값으로 움직입니다.",
1077
+ "figure.label.data-sample-in-preview": "미리보기에 적용",
1078
+ "figure.text.data-value-not-a-record": "샘플 값의 형식이 맞지 않습니다.",
1079
+ "figure.text.data-value-property-unknown": "{property} 이름의 데이터 속성이 이 Figure에 없습니다.",
1080
+ "figure.text.data-value-type-mismatch": "{property} 속성의 값 종류가 맞지 않습니다.",
1081
+ "figure.text.data-value-out-of-range": "{property} 속성의 값이 범위를 벗어났습니다.",
1082
+ "figure.text.data-value-enum-unknown": "{property} 속성에 없는 값입니다.",
1083
+ "figure.text.data-id-invalid": "{id} ID는 쓸 수 없습니다. 영문자로 시작하고 영문자·숫자·-·_만 씁니다.",
1084
+ "figure.text.data-id-duplicate": "{id} ID가 이미 있습니다.",
1085
+ "figure.text.data-property-invalid-label": "{property} 속성의 이름을 적습니다.",
1086
+ "figure.text.data-property-invalid-unit": "{property} 속성의 단위가 맞지 않습니다.",
1087
+ "figure.text.data-property-invalid-min": "{property} 속성의 범위는 작은 수에서 큰 수로 적습니다.",
1088
+ "figure.text.data-property-invalid-default": "{property} 속성의 기본값이 범위나 값 목록 안에 있어야 합니다.",
1089
+ "figure.text.data-property-invalid-values": "{property} 속성의 값 목록은 하나 이상이고 서로 달라야 합니다.",
1090
+ "figure.text.data-property-absent": "{property} 이름의 데이터 속성이 이 Figure에 없습니다.",
1091
+ "figure.text.data-target-absent-state": "{id} 조작값이 없습니다. 이 조작값을 쓰는 연결을 먼저 삭제합니다.",
1092
+ "figure.text.data-target-absent-palette": "{id} 색이 없습니다.",
1093
+ "figure.text.data-target-absent-motion-clip": "{id} 동작 그룹이 없습니다.",
1094
+ "figure.text.data-target-absent-motion-speed": "{id} 동작 그룹이 없습니다.",
1095
+ "figure.text.data-target-duplicate": "{a} 연결과 {b} 연결이 같은 대상 {id}에 씁니다. 대상 하나에 연결은 하나입니다.",
1096
+ "figure.text.data-transform-kind": "이 변환은 이 속성과 대상에 쓸 수 없습니다. 숫자는 그대로나 배율로, 선택값과 참/거짓은 값마다 정합니다.",
1097
+ "figure.text.data-transform-unit": "단위가 다릅니다 ({from} → {to}). 배율과 더하기로 바꾸는 방법을 적습니다.",
1098
+ "figure.text.data-transform-range": "결과가 {min} ~ {max}가 됩니다. 대상은 {low} ~ {high}만 받습니다.",
1099
+ "figure.text.data-transform-cases": "값마다 한 번씩 정합니다. {value} 값이 빠졌거나 겹쳤거나 속성에 없는 값입니다.",
1100
+ "figure.text.data-transform-output": "{value} 값은 대상이 받는 값이 아닙니다.",
1101
+ "figure.text.data-property-connected": "{property} 속성을 읽는 연결을 먼저 삭제합니다: {connections}.",
1102
+ "figure.text.motion-clip-absent": "{clip} 클립은 {group} 그룹에 넣을 수 있는 클립이 아닙니다.",
1103
+ "figure.text.motion-clip-shared": "{clip} 클립은 이미 {group} 그룹에 있습니다. 클립은 한 그룹에만 넣습니다.",
1104
+ "figure.text.driver-writers": "{clips} 클립이 모두 {state} 조작값을 움직입니다. 같은 조작값을 움직이는 클립은 한 동작 그룹에 넣습니다.",
1105
+ "figure.text.driver-clip-required": "{state} 조작값을 여러 클립({clips})이 움직입니다. 어느 클립의 트랙인지 고릅니다.",
1106
+ "figure.text.motion-setting-group-unknown": "{group} 동작 그룹이 없습니다.",
1107
+ "figure.text.motion-setting-clip-not-in-group": "{clip} 클립은 {group} 그룹의 클립이 아닙니다.",
1108
+ "figure.text.motion-setting-speed-out-of-range": "{group} 그룹의 배속은 0 ~ 3입니다.",
1109
+ "figure.text.motion-setting-group-selection-required": "{group} 그룹에서 재생할 클립을 고릅니다.",
1110
+ "figure.text.motion-key-outside-range": "{input} 조작값의 키 {value} 값이 범위 {min} ~ {max} 밖입니다. 그룹의 클립은 범위 안의 키만 씁니다.",
1111
+ "figure.label.tab-short-data": "데이터"
1028
1112
  }