@things-factory/figure-service 10.1.46 → 10.1.48
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.
- package/dist-server/service/figure/figure-format.d.ts +3 -2
- package/dist-server/service/figure/figure-format.js +23 -5
- package/dist-server/service/figure/figure-format.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/server/service/figure/figure-format.test.ts +35 -9
- package/server/service/figure/figure-format.ts +24 -6
- package/server/service/figure/figure-propose.test.ts +2 -1
- package/translations/en.json +3 -0
- package/translations/ko.json +22 -19
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/figure-service",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.48",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"@things-factory:registry": "https://registry.npmjs.org"
|
|
@@ -23,15 +23,15 @@
|
|
|
23
23
|
"migration:create": "node ../../node_modules/typeorm/cli.js migration:create ./server/migrations/migration"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@hatiolab/figure-model": "^0.1.
|
|
26
|
+
"@hatiolab/figure-model": "^0.1.54",
|
|
27
27
|
"@operato/typeorm-history": "^10.0.0",
|
|
28
28
|
"@things-factory/ai-client-base": "^10.1.39",
|
|
29
|
-
"@things-factory/auth-base": "^10.1.
|
|
29
|
+
"@things-factory/auth-base": "^10.1.47",
|
|
30
30
|
"@things-factory/env": "^10.1.20",
|
|
31
|
-
"@things-factory/shell": "^10.1.
|
|
31
|
+
"@things-factory/shell": "^10.1.47",
|
|
32
32
|
"graphql-scalars": "^1.22.4",
|
|
33
33
|
"graphql-upload": "^15.0.2",
|
|
34
34
|
"koa-router": "^7.4.0"
|
|
35
35
|
},
|
|
36
|
-
"gitHead": "
|
|
36
|
+
"gitHead": "448b0d568a1816d25aa49f3b6233d8086d6011f9"
|
|
37
37
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - V3 자산도 저장·발행·되돌리기·소비처를 지난다. 이 넷은 전에 V2 만 지날 수 있었다.
|
|
11
11
|
* - 예산 칸(`triangles`·`groups`)은 두 판에서 같은 뜻이다.
|
|
12
12
|
*/
|
|
13
|
-
import { createV3Asset, addV3Box, addV3Primitive, serializeV3Asset, applyV3Authoring, proposeV3Occupancy, placeholderType } from '@hatiolab/figure-model'
|
|
13
|
+
import { createV3Asset, addV3Box, addV3Primitive, serializeV3Asset, applyV3Authoring, proposeV3Occupancy, placeholderType, v3ScoreOf } from '@hatiolab/figure-model'
|
|
14
14
|
import type { V3Asset } from '@hatiolab/figure-model'
|
|
15
15
|
|
|
16
16
|
import { consumablePayloadOf, formatOf, inspectAnySource, parseAnySource } from './figure-format'
|
|
@@ -89,8 +89,10 @@ describe('figure-format — 판을 알아보는 문', () => {
|
|
|
89
89
|
expect(parsed.derived.groups).toBe(1)
|
|
90
90
|
})
|
|
91
91
|
|
|
92
|
-
it('V3 의 점수 칸은
|
|
93
|
-
|
|
92
|
+
it('V3 의 점수 칸은 v3ScoreOf 다 — 저작면의 성숙도 탭과 같은 수(수석 아키텍트 판정 09-24)', () => {
|
|
93
|
+
const score = parseAnySource(v3Source()).derived.score
|
|
94
|
+
expect(score).toBe(v3ScoreOf(v3Asset())!.score)
|
|
95
|
+
expect(score).toBeGreaterThan(0)
|
|
94
96
|
})
|
|
95
97
|
|
|
96
98
|
it('형식을 어긴 V3 자산은 거절한다', () => {
|
|
@@ -151,9 +153,9 @@ describe('figure-format — 판을 알아보는 문', () => {
|
|
|
151
153
|
const sampled = inspection.findings.find(f => f.code === 'state-range-sampled')
|
|
152
154
|
expect(sampled).toBeDefined()
|
|
153
155
|
expect(sampled!.blocking).toBe(false)
|
|
154
|
-
/* 증명을 막은
|
|
155
|
-
expect(sampled!.at).
|
|
156
|
-
expect(sampled!.why).not.
|
|
156
|
+
/* 증명을 막은 움직임은 사람이 붙인 이름으로 — 연산 이름과 영어 설명은 화면에 나가지 않는다. */
|
|
157
|
+
expect(sampled!.at).toBe('wheel')
|
|
158
|
+
expect(`${sampled!.at}${sampled!.why}`).not.toMatch(/axis-turn@1|state-dependent|[a-z]+@\d/)
|
|
157
159
|
})
|
|
158
160
|
|
|
159
161
|
it('정본이 없으면 전과 같이 그 말로 막는다', () => {
|
|
@@ -267,13 +269,29 @@ describe('figure-format — 판을 알아보는 문', () => {
|
|
|
267
269
|
const finding = inspectAnySource(splitting(), null, translatorOf(KO)).findings.find(f => f.code === 'parts-split-on-resize')
|
|
268
270
|
|
|
269
271
|
expect(finding?.blocking).toBe(true)
|
|
270
|
-
expect(finding!.message).toBe("크기를 Y 로 2배로 하면 'lamp'
|
|
272
|
+
expect(finding!.message).toBe("크기를 Y 로 2배로 하면 'lamp' 부품이 나머지에서 떨어집니다.")
|
|
271
273
|
expect(finding!.why).toMatch(/부서진/)
|
|
272
274
|
expect(finding!.how).toContain("'lamp'")
|
|
273
275
|
expect(finding!.how).toContain('여기에 고정')
|
|
274
276
|
expect(`${finding!.message}${finding!.why}${finding!.how}`).not.toMatch(/come away|piece/)
|
|
275
277
|
})
|
|
276
278
|
|
|
279
|
+
/* 한 비율로 커진다고 선언한 도형에서 제 숫자를 가진 부품(수석 아키텍트 판정 2026-09-24 조건 ②). */
|
|
280
|
+
it('한 비율로 커진다고 선언한 도형에서 따라 커지지 않는 부품을 막고 이름을 댄다', () => {
|
|
281
|
+
let asset = createV3Asset('Uniform')
|
|
282
|
+
asset = addV3Box(asset, { id: 'body', size: { x: 100, y: 100, z: 100 }, position: { x: 0, y: 50, z: 0 } })
|
|
283
|
+
for (const axis of ['x', 'y', 'z']) {
|
|
284
|
+
asset.document.model.inputs.push({ id: `size.${axis}`, unit: 'mm', min: 1, max: 20000, role: 'design' } as never)
|
|
285
|
+
asset.designInputs[`size.${axis}`] = 100
|
|
286
|
+
}
|
|
287
|
+
asset = applyV3Authoring(asset, { kind: 'scale-uniformly' } as never)
|
|
288
|
+
const finding = inspectAnySource(serializeV3Asset(asset), null, translatorOf(KO)).findings.find(f => f.code === 'sizing-not-uniform')
|
|
289
|
+
|
|
290
|
+
expect(finding?.blocking).toBe(true)
|
|
291
|
+
expect(finding!.message).toBe("이 도형은 한 비율로 커지는데, 크기를 X 로 2배로 하면 'body' 부품은 같이 커지지 않습니다.")
|
|
292
|
+
expect(finding!.how).toContain('크기에 비례')
|
|
293
|
+
})
|
|
294
|
+
|
|
277
295
|
/* The server's translator is i18next with its defaults, which escape values for HTML. The panel draws text. */
|
|
278
296
|
it('부품 이름의 따옴표가 HTML 로 바뀌지 않는다 — 서버와 같은 i18next 로', async () => {
|
|
279
297
|
const i18next = require('i18next').createInstance()
|
|
@@ -287,10 +305,18 @@ describe('figure-format — 판을 알아보는 문', () => {
|
|
|
287
305
|
})
|
|
288
306
|
const finding = inspectAnySource(splitting(), null, i18next.t.bind(i18next)).findings.find(f => f.code === 'parts-split-on-resize')!
|
|
289
307
|
|
|
290
|
-
expect(finding.message).toBe("크기를 Y 로 2배로 하면 'lamp'
|
|
308
|
+
expect(finding.message).toBe("크기를 Y 로 2배로 하면 'lamp' 부품이 나머지에서 떨어집니다.")
|
|
291
309
|
expect(`${finding.message}${finding.how}`).not.toContain(''')
|
|
292
310
|
})
|
|
293
311
|
|
|
312
|
+
/* A particle fixed after a quoted name is wrong for half the names ('그리퍼' 이). A noun after the name is always right. */
|
|
313
|
+
it('따옴표로 싼 이름 바로 뒤에 조사가 오지 않는다 — 이름 뒤에는 명사를 둔다', () => {
|
|
314
|
+
const particle = /'\{[a-z]+\}'\s*(이|가|을|를|은|는|과|와|의|로|으로)(\s|$|[.,])/
|
|
315
|
+
const bare = /\{parts\}\s*(이|가|을|를|은|는)(\s|$)/
|
|
316
|
+
const offending = Object.entries(KO).filter(([key, text]) => key.startsWith('figure.gate.') && (particle.test(text) || bare.test(text)))
|
|
317
|
+
expect(offending).toEqual([])
|
|
318
|
+
})
|
|
319
|
+
|
|
294
320
|
it('번역이 없으면 영어로 답한다 — 키를 찍지 않는다', () => {
|
|
295
321
|
const finding = inspectAnySource(outside()).findings.find(f => f.code === 'part-outside-occupancy')!
|
|
296
322
|
|
|
@@ -308,7 +334,7 @@ describe('figure-format — 판을 알아보는 문', () => {
|
|
|
308
334
|
const finding = inspectAnySource(v3Source(), null, translatorOf(KO), placeholder).findings.find(f => f.code === 'type-is-placeholder')
|
|
309
335
|
|
|
310
336
|
expect(finding?.blocking).toBe(true)
|
|
311
|
-
expect(finding!.message).toBe(`'${placeholder}'
|
|
337
|
+
expect(finding!.message).toBe(`'${placeholder}' 타입은 처음 저장할 때 붙은 임시 타입입니다.`)
|
|
312
338
|
/* 화면의 칸 이름은 「타입」이다(`figure.label.figure-type`). */
|
|
313
339
|
expect(finding!.how).toContain('타입 칸')
|
|
314
340
|
expect(inspectAnySource(v3Source(), null, translatorOf(KO), 'ROLLER_CONVEYOR').blocked).toBe(false)
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* No conversion. This door reads what is stored; turning a V2 asset into a V3 one is `convertV2ToV3`, run
|
|
21
21
|
* deliberately against a recoverable copy (the designer's step 3), never as a side effect of a save.
|
|
22
22
|
*/
|
|
23
|
-
import { compile, compileV3Asset, inspectV3Asset, isPlaceholderType, parseV3Asset, serializeV3Asset, v3ContractChanges, v3CostOf } from '@hatiolab/figure-model'
|
|
23
|
+
import { compile, compileV3Asset, inspectV3Asset, isPlaceholderType, parseV3Asset, serializeV3Asset, v3ContractChanges, v3CostOf, v3ScoreOf } from '@hatiolab/figure-model'
|
|
24
24
|
import type { V3Asset, V3ContractChange, V3ContractError, V3Violation } from '@hatiolab/figure-model'
|
|
25
25
|
|
|
26
26
|
import type { GateFinding, Inspection } from './figure-gate.js'
|
|
@@ -63,8 +63,9 @@ export function formatOf(sourceJson: string): FigureFormat {
|
|
|
63
63
|
* Validates a source of either format and computes what the catalogue shows.
|
|
64
64
|
*
|
|
65
65
|
* A V3 asset is measured with `v3CostOf`, which charges the same per-shape estimate V2 charges, so the
|
|
66
|
-
* budget columns mean one thing across the two formats. `score` is
|
|
67
|
-
*
|
|
66
|
+
* budget columns mean one thing across the two formats. Its `score` is `v3ScoreOf`'s -- the headroom against
|
|
67
|
+
* placing `LIMITS.screenInstances` of it on one screen (chief architect, 2026-09-24), the same number the
|
|
68
|
+
* modeller's maturity tab shows. An asset that draws nothing has no score, not a full one.
|
|
68
69
|
*
|
|
69
70
|
* @throws {FigureSourceError} when the source breaks its own format.
|
|
70
71
|
*/
|
|
@@ -82,10 +83,11 @@ export function parseAnySource(sourceJson: string): ParsedSource & { format: Fig
|
|
|
82
83
|
])
|
|
83
84
|
}
|
|
84
85
|
const cost = v3CostOf(asset)
|
|
86
|
+
const score = v3ScoreOf(asset)
|
|
85
87
|
return {
|
|
86
88
|
format: 'v3',
|
|
87
89
|
source: serializeV3Asset(asset),
|
|
88
|
-
derived: { score: null as unknown as number, triangles: cost.triangles, groups: cost.groups },
|
|
90
|
+
derived: { score: (score?.score ?? null) as unknown as number, triangles: cost.triangles, groups: cost.groups },
|
|
89
91
|
violations: []
|
|
90
92
|
}
|
|
91
93
|
}
|
|
@@ -175,6 +177,18 @@ function v3Wording(violation: V3Violation, say: Say, hasParts: boolean): Pick<Ga
|
|
|
175
177
|
how: say(key('how'), { parts }, `Fasten ${parts} to a face of the part they rest on, or press "fix here" under placement, so they stay put when the figure is resized.`)
|
|
176
178
|
}
|
|
177
179
|
}
|
|
180
|
+
case 'SIZING_NOT_UNIFORM': {
|
|
181
|
+
/* The figure declares it grows by one ratio; these parts keep a size or place of their own (ruling 2026-09-24). */
|
|
182
|
+
const resize = violation.resize!
|
|
183
|
+
const parts = (violation.parts ?? []).map(one => `'${one}'`).join(', ')
|
|
184
|
+
const axes = resize.axes.map(one => one.toUpperCase()).join('·')
|
|
185
|
+
const factor = resize.factor
|
|
186
|
+
return {
|
|
187
|
+
message: say(key('message'), { parts, axes, factor }, `The figure grows by one ratio, but at ${factor}x on ${axes} ${parts} do not grow with it.`),
|
|
188
|
+
why: say(key('why'), {}, 'A figure that grows by one ratio keeps its proportions at any size. A part with a size or place of its own comes apart from the rest.'),
|
|
189
|
+
how: say(key('how'), { parts }, `Let ${parts} follow the figure's size ("in proportion to size" under size and placement), or remove their own fixed numbers.`)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
178
192
|
}
|
|
179
193
|
}
|
|
180
194
|
|
|
@@ -252,8 +266,12 @@ export function inspectAnySource(
|
|
|
252
266
|
message: say('figure.gate.state-range-sampled.message', { states }, `The whole range of motion could not be proven, so ${states} poses were checked.`),
|
|
253
267
|
why: say('figure.gate.state-range-sampled.why', {}, 'Between the poses that were checked, a part may still go past the declared space.'),
|
|
254
268
|
how: say('figure.gate.state-range-sampled.how', {}, 'Re-author the motion that could not be proven, and the whole range can be proven.'),
|
|
255
|
-
/*
|
|
256
|
-
|
|
269
|
+
/*
|
|
270
|
+
Which motions stopped the proof, by the name a person gave them: the kernel reports each operation it could
|
|
271
|
+
not prove (`j2-shoulder.chain: compose@1 turns …`), and the screen showed that English list as it was. The
|
|
272
|
+
name before the first dot is the joint or part; the operations stay in the logs.
|
|
273
|
+
*/
|
|
274
|
+
at: [...new Set((inspection.coverage.unprovable ?? []).map(entry => entry.split(':')[0]!.split('.')[0]!.trim()).filter(Boolean))].join(' · ') || undefined,
|
|
257
275
|
blocking: false
|
|
258
276
|
})
|
|
259
277
|
}
|
|
@@ -176,7 +176,8 @@ describe('프롬프트는 형식을 따라간다 — 손으로 적은 목록이
|
|
|
176
176
|
*/
|
|
177
177
|
V3_OPERATORS: 'V3 커널 연산자 이름이다 — 제안기가 V2 정본을 쓰는 동안은 저작자가 적는 값이 아니다',
|
|
178
178
|
V3_NON_AFFINE_OPERATORS: '위 목록의 부분집합이다 — 어느 연산이 다중선형을 안 나르나는 관문이 알 일이다',
|
|
179
|
-
V3_STATE_NON_AFFINE_OPERATORS: '같다'
|
|
179
|
+
V3_STATE_NON_AFFINE_OPERATORS: '같다',
|
|
180
|
+
V3_BUDGET_KEYS: 'V3 등급이 재는 예산의 이름이다 — 채점 결과이지 저작자가 적는 값이 아니다'
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
it('저작에 쓰이는 열거는 값 하나도 빠짐없이 프롬프트에 있다', () => {
|
package/translations/en.json
CHANGED
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
"figure.gate.parts-split-on-resize.how": "Fasten {parts} to a face of the part they rest on, or press “fix here” under placement, so they stay put when the figure is resized.",
|
|
15
15
|
"figure.gate.parts-split-on-resize.message": "At {factor}x on {axes}, {parts} come away from the rest of the figure.",
|
|
16
16
|
"figure.gate.parts-split-on-resize.why": "A figure placed at another size shows those parts hanging in the air — to a person, it looks broken.",
|
|
17
|
+
"figure.gate.sizing-not-uniform.how": "Let {parts} follow the figure's size (“in proportion to size” under size and placement), or remove their own fixed numbers.",
|
|
18
|
+
"figure.gate.sizing-not-uniform.message": "The figure grows by one ratio, but at {factor}x on {axes} {parts} do not grow with it.",
|
|
19
|
+
"figure.gate.sizing-not-uniform.why": "A figure that grows by one ratio keeps its proportions at any size. A part with a size or place of its own comes apart from the rest.",
|
|
17
20
|
"figure.gate.placement-face-tilted.how": "Choose a mounting face parallel to the plane, or turn '{part}' upright.",
|
|
18
21
|
"figure.gate.placement-face-tilted.message": "The mounting face of '{part}' is tilted {degrees}° from the plane the figure stands on.",
|
|
19
22
|
"figure.gate.placement-face-tilted.why": "A tilted face cannot lie on the plane, so how the figure stands is not settled.",
|
package/translations/ko.json
CHANGED
|
@@ -5,23 +5,26 @@
|
|
|
5
5
|
"figure.gate.occupancy-undeclared.how-no-parts": "먼저 도형을 추가한 뒤, 「부품에 맞추기」로 공간을 선언하세요.",
|
|
6
6
|
"figure.gate.occupancy-undeclared.message": "이 도형은 차지하는 공간을 아직 선언하지 않았습니다.",
|
|
7
7
|
"figure.gate.occupancy-undeclared.why": "공간 선언이 없으면 도면에 놓일 때의 배율을 정할 수 없습니다.",
|
|
8
|
-
"figure.gate.part-outside-occupancy.how": "「부품에 맞추기」로 공간을 부품에서 다시 선언하거나, '{part}'
|
|
8
|
+
"figure.gate.part-outside-occupancy.how": "「부품에 맞추기」로 공간을 부품에서 다시 선언하거나, '{part}' 부품을 {axis} 로 {mm}mm 안으로 옮기세요.",
|
|
9
9
|
"figure.gate.part-outside-occupancy.message": "'{part}' 부품이 공간 선언을 {axis} 로 {mm}mm 넘습니다.",
|
|
10
10
|
"figure.gate.part-outside-occupancy.why": "공간 선언이 도면에 놓일 때의 배율을 정합니다. 넘은 만큼은 그 배율에 들어가지 않아서, 도면에서 이 부품이 도형에 주어진 자리 밖으로 나갑니다.",
|
|
11
|
-
"figure.gate.parts-off-placement-face.how": "'{part}'
|
|
11
|
+
"figure.gate.parts-off-placement-face.how": "'{part}' 부품을 Y 로 {mm}mm 옮겨 그 면을 평면에 맞추거나, 장착면을 다른 면으로 다시 정하세요.",
|
|
12
12
|
"figure.gate.parts-off-placement-face.message": "장착면으로 정한 '{part}' 부품의 면이 놓이는 평면에서 {mm}mm 떨어져 있습니다.",
|
|
13
13
|
"figure.gate.parts-off-placement-face.why": "도형은 그 면으로 놓인다고 선언돼 있습니다. 닿지 않으면 도면에서 그만큼 떠 보이고, 자리에 놓인 물건도 함께 뜹니다.",
|
|
14
|
-
"figure.gate.parts-split-on-resize.how": "{parts}
|
|
15
|
-
"figure.gate.parts-split-on-resize.message": "크기를 {axes} 로 {factor}배로 하면 {parts}
|
|
14
|
+
"figure.gate.parts-split-on-resize.how": "{parts} 부품을 올려 둔 부품의 면에 붙이거나, 부품의 「위치와 크기」에서 「여기에 고정」을 누르면 크기를 바꿔도 제자리에 있습니다.",
|
|
15
|
+
"figure.gate.parts-split-on-resize.message": "크기를 {axes} 로 {factor}배로 하면 {parts} 부품이 나머지에서 떨어집니다.",
|
|
16
16
|
"figure.gate.parts-split-on-resize.why": "다른 크기로 놓으면 그 부품이 공중에 떠 보입니다. 보는 사람에게는 부서진 도형입니다.",
|
|
17
|
-
"figure.gate.
|
|
17
|
+
"figure.gate.sizing-not-uniform.how": "{parts} 부품이 도형 크기를 따르게 하세요(「위치와 크기」의 「크기에 비례」). 제 고정 숫자를 가진 부품은 한 비율로 커지지 않습니다.",
|
|
18
|
+
"figure.gate.sizing-not-uniform.message": "이 도형은 한 비율로 커지는데, 크기를 {axes} 로 {factor}배로 하면 {parts} 부품은 같이 커지지 않습니다.",
|
|
19
|
+
"figure.gate.sizing-not-uniform.why": "한 비율로 커지는 도형은 어떤 크기에서도 비례를 지킵니다. 제 크기나 자리를 따로 가진 부품은 나머지와 어긋납니다.",
|
|
20
|
+
"figure.gate.placement-face-tilted.how": "평면과 나란한 면을 장착면으로 다시 정하거나, '{part}' 부품을 바로 세우세요.",
|
|
18
21
|
"figure.gate.placement-face-tilted.message": "장착면으로 정한 '{part}' 부품의 면이 놓이는 평면에서 {degrees}° 기울어 있습니다.",
|
|
19
22
|
"figure.gate.placement-face-tilted.why": "기운 면은 평면에 닿을 수 없어서, 도형이 어떤 자세로 놓이는지 정해지지 않습니다.",
|
|
20
23
|
"figure.gate.state-range-sampled.how": "증명하지 못한 움직임을 다시 저작하면 범위 전체를 증명할 수 있습니다.",
|
|
21
24
|
"figure.gate.state-range-sampled.message": "움직이는 범위 전체를 증명하지 못해 자세 {states}개만 검사했습니다.",
|
|
22
25
|
"figure.gate.state-range-sampled.why": "검사한 자세 사이에서 부품이 공간 선언을 넘을 수 있습니다.",
|
|
23
26
|
"figure.gate.type-is-placeholder.how": "타입 칸에서 타입을 정한 뒤 발행하세요.",
|
|
24
|
-
"figure.gate.type-is-placeholder.message": "'{type}'
|
|
27
|
+
"figure.gate.type-is-placeholder.message": "'{type}' 타입은 처음 저장할 때 붙은 임시 타입입니다.",
|
|
25
28
|
"figure.gate.type-is-placeholder.why": "보드는 이 타입으로 도형을 부르고, 발행한 뒤에는 바꿀 수 없습니다.",
|
|
26
29
|
"figure.gate.v3-released-not-compared.message": "직전 발행판과 견주지 못했습니다. V3 도형은 발행 사이의 비교가 아직 없습니다.",
|
|
27
30
|
"figure.gate.v3-released-not-compared.why": "이 발행이 이미 놓인 인스턴스를 깨는지는 확인되지 않았습니다.",
|
|
@@ -37,30 +40,30 @@
|
|
|
37
40
|
"figure.gate.contract.size-range-narrowed.why": "새 범위 밖의 크기로 놓인 인스턴스는 그려지지 않습니다. 어떤 크기로 놓였는지는 기록되지 않습니다.",
|
|
38
41
|
"figure.gate.contract.authored-size-changed.message": "저작한 크기가 {before} 에서 {after} 로 바뀌었습니다.",
|
|
39
42
|
"figure.gate.contract.authored-size-changed.why": "놓인 인스턴스는 제 크기를 그대로 둡니다. 깊이를 따로 정하지 않은 인스턴스는 이 크기에서 깊이를 받습니다.",
|
|
40
|
-
"figure.gate.contract.state-input-gone.message": "
|
|
43
|
+
"figure.gate.contract.state-input-gone.message": "'{id}' 조작값이 없어졌습니다.",
|
|
41
44
|
"figure.gate.contract.state-input-gone.why": "놓인 인스턴스와 커넥터가 이 이름으로 값을 줍니다. 이 발행 뒤에는 그 값이 아무 일도 하지 않고, 화면에는 까닭이 나오지 않습니다.",
|
|
42
|
-
"figure.gate.contract.state-unit-changed.message": "
|
|
45
|
+
"figure.gate.contract.state-unit-changed.message": "'{id}' 조작값의 단위가 {before} 에서 {after} 로 바뀌었습니다.",
|
|
43
46
|
"figure.gate.contract.state-unit-changed.why": "저장된 값은 수를 그대로 가집니다. 단위가 바뀌면 같은 수가 다른 뜻이 됩니다.",
|
|
44
|
-
"figure.gate.contract.state-range-narrowed.message": "
|
|
47
|
+
"figure.gate.contract.state-range-narrowed.message": "'{id}' 조작값의 범위가 {before} 에서 {after} 로 좁아졌습니다.",
|
|
45
48
|
"figure.gate.contract.state-range-narrowed.why": "놓인 인스턴스에 새 범위 밖의 값이 이미 저장돼 있을 수 있습니다.",
|
|
46
|
-
"figure.gate.contract.state-input-added.message": "
|
|
49
|
+
"figure.gate.contract.state-input-added.message": "'{id}' 조작값이 새로 생겼습니다.",
|
|
47
50
|
"figure.gate.contract.state-input-added.why": "값을 주지 않은 인스턴스는 기본값에 섭니다.",
|
|
48
|
-
"figure.gate.contract.clip-gone.message": "
|
|
51
|
+
"figure.gate.contract.clip-gone.message": "'{clip}' 클립이 없어졌습니다.",
|
|
49
52
|
"figure.gate.contract.clip-gone.why": "놓인 인스턴스는 이름으로 클립을 재생합니다. 이 발행 뒤에는 그 클립이 아무것도 재생하지 않습니다.",
|
|
50
|
-
"figure.gate.contract.clip-added.message": "
|
|
53
|
+
"figure.gate.contract.clip-added.message": "'{clip}' 클립이 새로 생겼습니다.",
|
|
51
54
|
"figure.gate.contract.clip-added.why": "인스턴스가 재생하기 전에는 멈춰 있습니다.",
|
|
52
|
-
"figure.gate.contract.binding-gone.message": "
|
|
55
|
+
"figure.gate.contract.binding-gone.message": "'{binding}' 자리가 없어졌습니다.",
|
|
53
56
|
"figure.gate.contract.binding-gone.why": "놓인 인스턴스에서 그 자리에 든 것이 갈 곳을 잃습니다.",
|
|
54
|
-
"figure.gate.contract.binding-role-changed.message": "
|
|
57
|
+
"figure.gate.contract.binding-role-changed.message": "'{binding}' 자리의 역할이 {before} 에서 {after} 로 바뀌었습니다.",
|
|
55
58
|
"figure.gate.contract.binding-role-changed.why": "그 자리에 든 것은 옛 역할에 맞춰 놓였습니다.",
|
|
56
|
-
"figure.gate.contract.binding-capacity-reduced.message": "
|
|
59
|
+
"figure.gate.contract.binding-capacity-reduced.message": "'{binding}' 자리가 {before} 개 대신 {after} 개를 받습니다.",
|
|
57
60
|
"figure.gate.contract.binding-capacity-reduced.why": "없어진 칸에 든 것이 갈 곳을 잃습니다.",
|
|
58
|
-
"figure.gate.contract.binding-accepts-narrowed.message": "
|
|
61
|
+
"figure.gate.contract.binding-accepts-narrowed.message": "'{binding}' 자리가 더는 받지 않는 종류: {types}.",
|
|
59
62
|
"figure.gate.contract.binding-accepts-narrowed.why": "이미 든 것은 그대로 있고, 그 타입의 다음 것부터 거절됩니다.",
|
|
60
|
-
"figure.gate.contract.binding-added.message": "
|
|
63
|
+
"figure.gate.contract.binding-added.message": "'{binding}' 자리가 새로 생겼습니다.",
|
|
61
64
|
"figure.gate.contract.binding-added.why": "놓인 인스턴스는 아직 이 자리를 모르고 그대로 서 있습니다.",
|
|
62
|
-
"figure.gate.contract.capability-gone.message": "
|
|
65
|
+
"figure.gate.contract.capability-gone.message": "'{name}' 능력이 없어졌습니다.",
|
|
63
66
|
"figure.gate.contract.capability-gone.why": "씬이 이 이름으로 동작을 붙입니다. 놓인 인스턴스가 그 동작을 잃습니다.",
|
|
64
|
-
"figure.gate.contract.capability-added.message": "
|
|
67
|
+
"figure.gate.contract.capability-added.message": "'{name}' 능력이 새로 생겼습니다.",
|
|
65
68
|
"figure.gate.contract.capability-added.why": "도형이 할 수 있는 일이 늘어납니다. 놓인 인스턴스는 그대로 서 있습니다."
|
|
66
69
|
}
|