@things-factory/figure-service 10.1.157 → 10.1.160
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/index.d.ts +1 -0
- package/dist-server/index.js +2 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/service/figure/figure-namespace-resolver.d.ts +10 -0
- package/dist-server/service/figure/figure-namespace-resolver.js +65 -0
- package/dist-server/service/figure/figure-namespace-resolver.js.map +1 -0
- package/dist-server/service/figure/figure-namespace.d.ts +20 -2
- package/dist-server/service/figure/figure-namespace.js +45 -6
- package/dist-server/service/figure/figure-namespace.js.map +1 -1
- package/dist-server/service/figure/figure-subject.d.ts +12 -0
- package/dist-server/service/figure/figure-subject.js +24 -0
- package/dist-server/service/figure/figure-subject.js.map +1 -0
- package/dist-server/service/figure/index.d.ts +2 -1
- package/dist-server/service/figure/index.js +2 -1
- package/dist-server/service/figure/index.js.map +1 -1
- package/dist-server/service/index.d.ts +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/server/index.ts +2 -0
- package/server/service/figure/figure-namespace-db.test.ts +45 -6
- package/server/service/figure/figure-namespace-resolver.ts +41 -0
- package/server/service/figure/figure-namespace.ts +63 -7
- package/server/service/figure/figure-subject.test.ts +41 -0
- package/server/service/figure/figure-subject.ts +21 -0
- package/server/service/figure/index.ts +2 -1
- package/translations/en.json +1 -0
- package/translations/es.json +1 -0
- package/translations/ja.json +1 -0
- package/translations/ko.json +1 -0
- package/translations/ms.json +1 -0
- package/translations/zh.json +1 -0
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.160",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public",
|
|
6
6
|
"@things-factory:registry": "https://registry.npmjs.org"
|
|
@@ -27,16 +27,16 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@hatiolab/figure-model": "^0.6.5",
|
|
29
29
|
"@operato/typeorm-history": "^10.0.0",
|
|
30
|
-
"@things-factory/ai-client-base": "^10.1.
|
|
31
|
-
"@things-factory/attachment-base": "^10.1.
|
|
32
|
-
"@things-factory/auth-base": "^10.1.
|
|
30
|
+
"@things-factory/ai-client-base": "^10.1.159",
|
|
31
|
+
"@things-factory/attachment-base": "^10.1.159",
|
|
32
|
+
"@things-factory/auth-base": "^10.1.159",
|
|
33
33
|
"@things-factory/env": "^10.1.149",
|
|
34
|
-
"@things-factory/setting-base": "^10.1.
|
|
34
|
+
"@things-factory/setting-base": "^10.1.159",
|
|
35
35
|
"@things-factory/shell": "^10.1.157",
|
|
36
36
|
"graphql-scalars": "^1.22.4",
|
|
37
37
|
"graphql-upload": "^15.0.2",
|
|
38
38
|
"jszip": "^3.10.1",
|
|
39
39
|
"koa-router": "^7.4.0"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "b5e86be374e8b913a3f2b8459b6001e8f44e345b"
|
|
42
42
|
}
|
package/server/index.ts
CHANGED
|
@@ -9,6 +9,8 @@ import './routes.js'
|
|
|
9
9
|
*/
|
|
10
10
|
import { registerFigureToolCategories } from './service/figure/figure-tools.js'
|
|
11
11
|
registerFigureToolCategories()
|
|
12
|
+
/* The figure the modeller has open, said to the AI each turn (design/ai/screen-pattern.md §2-1). */
|
|
13
|
+
import './service/figure/figure-subject.js'
|
|
12
14
|
|
|
13
15
|
/*
|
|
14
16
|
* The recipe templates are read once at start (ADR-0095, amended): what was served, and any file whose form is wrong,
|
|
@@ -90,15 +90,25 @@ describe('the namespace a figure is created under', () => {
|
|
|
90
90
|
expect(figure.type).toBe('conveyor')
|
|
91
91
|
})
|
|
92
92
|
|
|
93
|
-
it('
|
|
94
|
-
for (const value of [null, '
|
|
95
|
-
const { context } = await domainWith(value)
|
|
96
|
-
await
|
|
97
|
-
|
|
98
|
-
).rejects.toMatchObject({ code: 'figure-namespace-missing' })
|
|
93
|
+
it('with no setting, or an empty one, is the subdomain', async () => {
|
|
94
|
+
for (const value of [null, '', ' ']) {
|
|
95
|
+
const { domain, context } = await domainWith(value)
|
|
96
|
+
const figure = await new FigureMutation().saveFigure(releasable(), context, undefined, undefined, creationId(), undefined, 'conveyor')
|
|
97
|
+
expect(figure.namespace).toBe(domain.subdomain)
|
|
99
98
|
}
|
|
100
99
|
})
|
|
101
100
|
|
|
101
|
+
it('is refused by name when the setting is not a namespace, or there is none and the subdomain is not one either', async () => {
|
|
102
|
+
const { context } = await domainWith('Hatiolab.COM')
|
|
103
|
+
await expect(
|
|
104
|
+
new FigureMutation().saveFigure(releasable(), context, undefined, undefined, creationId(), undefined, 'conveyor')
|
|
105
|
+
).rejects.toMatchObject({ code: 'figure-namespace-missing' })
|
|
106
|
+
const odd = await dataSource!.getRepository(shell.Domain).save({ name: `odd-${++seq}`, subdomain: 'Odd_Sub' })
|
|
107
|
+
await expect(
|
|
108
|
+
new FigureMutation().saveFigure(releasable(), { state: { domain: odd, user: undefined, tx: dataSource!.manager } }, undefined, undefined, creationId(), undefined, 'conveyor')
|
|
109
|
+
).rejects.toMatchObject({ code: 'figure-namespace-missing' })
|
|
110
|
+
})
|
|
111
|
+
|
|
102
112
|
it('cannot change: a new figure or a release is refused while the setting disagrees with the figures already made', async () => {
|
|
103
113
|
const { domain, context } = await domainWith('hatiolab.com')
|
|
104
114
|
const draft = await new FigureMutation().saveFigure(releasable(), context, undefined, undefined, creationId(), undefined, 'conveyor')
|
|
@@ -120,6 +130,35 @@ describe('the namespace a figure is created under', () => {
|
|
|
120
130
|
})
|
|
121
131
|
})
|
|
122
132
|
|
|
133
|
+
describe('the namespace setting, as the setting screen reads and writes it', () => {
|
|
134
|
+
const resolver = () => new (loadCompiled('service/figure/figure-namespace-resolver').FigureNamespaceResolver)()
|
|
135
|
+
|
|
136
|
+
it('reads the subdomain when nothing is written, and is not fixed before a figure is made', async () => {
|
|
137
|
+
const { domain, context } = await domainWith(null)
|
|
138
|
+
expect(await resolver().figureNamespace(context)).toEqual({ namespace: domain.subdomain, written: '', subdomain: domain.subdomain, fixed: false })
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
it('writes a namespace, and empty goes back to the subdomain; a new figure takes what it says', async () => {
|
|
142
|
+
const { domain, context } = await domainWith(null)
|
|
143
|
+
expect(await resolver().setFigureNamespace(' hatiolab.com ', context)).toMatchObject({ namespace: 'hatiolab.com', written: 'hatiolab.com', fixed: false })
|
|
144
|
+
expect(await resolver().setFigureNamespace('', context)).toMatchObject({ namespace: domain.subdomain, written: '', fixed: false })
|
|
145
|
+
await resolver().setFigureNamespace('hatiolab.com', context)
|
|
146
|
+
const figure = await new FigureMutation().saveFigure(releasable(), context, undefined, undefined, creationId(), undefined, 'conveyor')
|
|
147
|
+
expect(figure.namespace).toBe('hatiolab.com')
|
|
148
|
+
expect(await resolver().figureNamespace(context)).toMatchObject({ namespace: 'hatiolab.com', fixed: true })
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('refuses what is not a namespace, and another namespace once figures carry one; the setting is left as it was', async () => {
|
|
152
|
+
const { context } = await domainWith('hatiolab.com')
|
|
153
|
+
await expect(resolver().setFigureNamespace('Hatiolab.COM', context)).rejects.toMatchObject({ code: 'figure-namespace-invalid' })
|
|
154
|
+
await new FigureMutation().saveFigure(releasable(), context, undefined, undefined, creationId(), undefined, 'conveyor')
|
|
155
|
+
await expect(resolver().setFigureNamespace('other.com', context)).rejects.toMatchObject({ code: 'figure-namespace-changed' })
|
|
156
|
+
expect(await resolver().figureNamespace(context)).toMatchObject({ namespace: 'hatiolab.com', written: 'hatiolab.com', fixed: true })
|
|
157
|
+
/* The same one again is not a change. */
|
|
158
|
+
await expect(resolver().setFigureNamespace('hatiolab.com', context)).resolves.toMatchObject({ namespace: 'hatiolab.com' })
|
|
159
|
+
})
|
|
160
|
+
})
|
|
161
|
+
|
|
123
162
|
describe('figureBlueprints answers each full name, an item or a refusal', () => {
|
|
124
163
|
it('gives released figures by full name and refuses the rest by cause, none taking another away', async () => {
|
|
125
164
|
const { context } = await domainWith('hatiolab.com')
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Arg, Ctx, Directive, Field, Mutation, ObjectType, Query, Resolver, UseMiddleware } from 'type-graphql'
|
|
2
|
+
|
|
3
|
+
import { getDataSource, SpeakRefusals } from '@things-factory/shell'
|
|
4
|
+
|
|
5
|
+
import { figureNamespaceStateOf, setFigureNamespace } from './figure-namespace.js'
|
|
6
|
+
|
|
7
|
+
@ObjectType({ description: "A domain's figure namespace, as the setting screen shows it (ADR-0114)." })
|
|
8
|
+
export class FigureNamespace {
|
|
9
|
+
@Field({ nullable: true, description: 'The namespace new figures are created under; null when neither the setting nor the subdomain is one.' })
|
|
10
|
+
namespace?: string
|
|
11
|
+
|
|
12
|
+
@Field({ description: 'The figure-namespace setting as written; empty when the subdomain is used.' })
|
|
13
|
+
written: string
|
|
14
|
+
|
|
15
|
+
@Field({ description: "The domain's subdomain, used when the setting is empty." })
|
|
16
|
+
subdomain: string
|
|
17
|
+
|
|
18
|
+
@Field({ description: "True once the domain's figures carry the namespace: it cannot be set any more." })
|
|
19
|
+
fixed: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@Resolver()
|
|
23
|
+
@UseMiddleware(SpeakRefusals)
|
|
24
|
+
export class FigureNamespaceResolver {
|
|
25
|
+
@Directive('@privilege(category: "figure", privilege: "query", domainOwnerGranted: true)')
|
|
26
|
+
@Query(returns => FigureNamespace, { description: "The domain's figure namespace and whether it can still be set." })
|
|
27
|
+
async figureNamespace(@Ctx() context: ResolverContext): Promise<FigureNamespace> {
|
|
28
|
+
const { domain, tx } = context.state
|
|
29
|
+
return (await figureNamespaceStateOf(tx ?? getDataSource().manager, domain)) as FigureNamespace
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@Directive('@transaction')
|
|
33
|
+
@Directive('@privilege(category: "setting", privilege: "mutation", domainOwnerGranted: true)')
|
|
34
|
+
@Mutation(returns => FigureNamespace, {
|
|
35
|
+
description: "Sets the domain's figure namespace. Empty takes the subdomain. Refused when it is not a namespace, or when the domain's figures already carry another."
|
|
36
|
+
})
|
|
37
|
+
async setFigureNamespace(@Arg('namespace') namespace: string, @Ctx() context: ResolverContext): Promise<FigureNamespace> {
|
|
38
|
+
const { domain, tx } = context.state
|
|
39
|
+
return (await setFigureNamespace(tx, domain, namespace)) as FigureNamespace
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityManager, Not } from 'typeorm'
|
|
1
|
+
import { EntityManager, IsNull, Not } from 'typeorm'
|
|
2
2
|
|
|
3
3
|
import { isFigureNamespace } from '@hatiolab/figure-model'
|
|
4
4
|
import { Domain, Refusal } from '@things-factory/shell'
|
|
@@ -13,14 +13,13 @@ export const FIGURE_NAMESPACE_SETTING = 'figure-namespace'
|
|
|
13
13
|
* The namespace a new figure is created under.
|
|
14
14
|
*
|
|
15
15
|
* It is read from the domain setting and copied onto the figure, so the full name `{namespace}/{type}` a board stores
|
|
16
|
-
* never moves.
|
|
17
|
-
*
|
|
16
|
+
* never moves. A domain with no setting, or an empty one, takes its subdomain (user, 2026-10-13: 「기본적으로는
|
|
17
|
+
* subdomain 명을 사용하면 되는데」) -- refused only when the subdomain is not a namespace either. A value written in
|
|
18
|
+
* the setting is used as it is, and refused when it is not a namespace. Either way it is set once: a value that differs
|
|
19
|
+
* from the namespace this domain's figures already carry is refused, so editing the setting cannot rename anything.
|
|
18
20
|
*/
|
|
19
21
|
export async function namespaceForNewFigure(tx: EntityManager, domain: Domain): Promise<string> {
|
|
20
|
-
const
|
|
21
|
-
where: { domain: { id: domain.id }, name: FIGURE_NAMESPACE_SETTING }
|
|
22
|
-
})
|
|
23
|
-
const namespace = setting?.value?.trim() ?? ''
|
|
22
|
+
const namespace = await chosenNamespace(tx, domain)
|
|
24
23
|
if (!isFigureNamespace(namespace)) {
|
|
25
24
|
throw new Refusal(
|
|
26
25
|
'figure-namespace-missing',
|
|
@@ -57,3 +56,60 @@ export function namespaceOf(figure: Figure): string {
|
|
|
57
56
|
}
|
|
58
57
|
return figure.namespace
|
|
59
58
|
}
|
|
59
|
+
|
|
60
|
+
/** What the setting says, or the subdomain when it says nothing: the namespace new figures are created under. */
|
|
61
|
+
async function chosenNamespace(tx: EntityManager, domain: Domain): Promise<string> {
|
|
62
|
+
return (await writtenNamespace(tx, domain)) || (domain.subdomain ?? '').trim()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function writtenNamespace(tx: EntityManager, domain: Domain): Promise<string> {
|
|
66
|
+
const setting = await tx.getRepository(Setting).findOne({
|
|
67
|
+
where: { domain: { id: domain.id }, name: FIGURE_NAMESPACE_SETTING }
|
|
68
|
+
})
|
|
69
|
+
return setting?.value?.trim() ?? ''
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** A domain's figure namespace as the setting screen shows it. */
|
|
73
|
+
export interface FigureNamespaceState {
|
|
74
|
+
/** What new figures get, or null when neither the setting nor the subdomain is a namespace. */
|
|
75
|
+
namespace: string | null
|
|
76
|
+
/** The setting as written; empty when the subdomain is used. */
|
|
77
|
+
written: string
|
|
78
|
+
subdomain: string
|
|
79
|
+
/** The domain's figures already carry the namespace, so it cannot be set any more. */
|
|
80
|
+
fixed: boolean
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export async function figureNamespaceStateOf(tx: EntityManager, domain: Domain): Promise<FigureNamespaceState> {
|
|
84
|
+
const written = await writtenNamespace(tx, domain)
|
|
85
|
+
const subdomain = (domain.subdomain ?? '').trim()
|
|
86
|
+
const carried = await tx.getRepository(Figure).findOne({ where: { domain: { id: domain.id }, namespace: Not(IsNull()) } })
|
|
87
|
+
const chosen = written || subdomain
|
|
88
|
+
return {
|
|
89
|
+
namespace: carried?.namespace ?? (isFigureNamespace(chosen) ? chosen : null),
|
|
90
|
+
written,
|
|
91
|
+
subdomain,
|
|
92
|
+
fixed: !!carried?.namespace
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Writes the domain's figure namespace (ADR-0114). Empty takes the subdomain. Refused when the value, or the subdomain
|
|
98
|
+
* it falls back to, is not a namespace, and when the domain's figures already carry another one.
|
|
99
|
+
*/
|
|
100
|
+
export async function setFigureNamespace(tx: EntityManager, domain: Domain, value: string): Promise<FigureNamespaceState> {
|
|
101
|
+
const written = value.trim()
|
|
102
|
+
const namespace = written || (domain.subdomain ?? '').trim()
|
|
103
|
+
if (!isFigureNamespace(namespace)) {
|
|
104
|
+
throw new Refusal(
|
|
105
|
+
'figure-namespace-invalid',
|
|
106
|
+
{ value: namespace },
|
|
107
|
+
'{value} is not a figure namespace. use lower-case letters, digits, dots and hyphens, for example hatiolab.com.'
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
await assertNamespaceUnchanged(tx, domain, namespace)
|
|
111
|
+
const repository = tx.getRepository(Setting)
|
|
112
|
+
const setting = await repository.findOne({ where: { domain: { id: domain.id }, name: FIGURE_NAMESPACE_SETTING } })
|
|
113
|
+
await repository.save({ ...setting, domain, name: FIGURE_NAMESPACE_SETTING, category: 'figure', value: written })
|
|
114
|
+
return figureNamespaceStateOf(tx, domain)
|
|
115
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The modeller's open figure, named for the AI (operato-application design/ai/screen-pattern.md §2-1).
|
|
3
|
+
*/
|
|
4
|
+
const mockFigures: any[] = [
|
|
5
|
+
{ id: 'f1', domain: 'plant-a', name: '선풍기', type: 'system/fan' },
|
|
6
|
+
{ id: 'f2', domain: 'plant-a', name: '', type: 'system/agv-lift' },
|
|
7
|
+
{ id: 'f3', domain: 'plant-b', name: '남의 도형', type: 'other/one' }
|
|
8
|
+
]
|
|
9
|
+
const mockRegistered: any[] = []
|
|
10
|
+
|
|
11
|
+
jest.mock('@things-factory/shell', () => ({
|
|
12
|
+
getRepository: (name: string) => {
|
|
13
|
+
if (name !== 'Figure') throw new Error(`unexpected repository ${name}`)
|
|
14
|
+
return {
|
|
15
|
+
findOne: async ({ where }: any) => mockFigures.find(one => one.id === where.id && one.domain === where.domain.id) ?? null
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}))
|
|
19
|
+
jest.mock('@things-factory/ai-client-base', () => ({ registerAssistantSubject: (one: any) => mockRegistered.push(one) }))
|
|
20
|
+
|
|
21
|
+
/* Required after the mocks hold their lists - an import would load it before them. */
|
|
22
|
+
const { figureSubject } = require('./figure-subject')
|
|
23
|
+
|
|
24
|
+
describe('the figure the modeller has open', () => {
|
|
25
|
+
it('★ is registered for the figure anchor when the package loads', () => {
|
|
26
|
+
expect(mockRegistered).toEqual([figureSubject])
|
|
27
|
+
expect(figureSubject.anchorType).toBe('figure')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('★ is named by its name, in this domain', async () => {
|
|
31
|
+
expect(await figureSubject.describe({ anchorId: 'f1', domain: { id: 'plant-a' } })).toEqual({ noun: 'figure', name: '선풍기' })
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('a figure with no name yet is called by its type', async () => {
|
|
35
|
+
expect(await figureSubject.describe({ anchorId: 'f2', domain: { id: 'plant-a' } })).toEqual({ noun: 'figure', name: 'system/agv-lift' })
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('★ another domain\'s figure is not named', async () => {
|
|
39
|
+
expect(await figureSubject.describe({ anchorId: 'f3', domain: { id: 'plant-a' } })).toBeUndefined()
|
|
40
|
+
})
|
|
41
|
+
})
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The figure the modeller has open, named for the AI (operato-application design/ai/screen-pattern.md §2-1).
|
|
3
|
+
*
|
|
4
|
+
* A conversation in the modeller is anchored to the open figure. Each turn the door reads this to say which figure
|
|
5
|
+
* that is, so the model starts from "the figure 선풍기 is open" and passes its id to the tools. A figure of another
|
|
6
|
+
* domain, or one that is gone, is not named.
|
|
7
|
+
*/
|
|
8
|
+
import { registerAssistantSubject } from '@things-factory/ai-client-base'
|
|
9
|
+
import { getRepository } from '@things-factory/shell'
|
|
10
|
+
|
|
11
|
+
/* By entity name - the entity's decorators stay out of what a test of this has to load. */
|
|
12
|
+
export const figureSubject = {
|
|
13
|
+
anchorType: 'figure',
|
|
14
|
+
async describe({ anchorId, domain }: { anchorId: string; domain: { id: string } }) {
|
|
15
|
+
const figure: any = await getRepository('Figure').findOne({ where: { id: anchorId, domain: { id: domain.id } } })
|
|
16
|
+
if (!figure) return undefined
|
|
17
|
+
return { noun: 'figure', name: figure.name || figure.type }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
registerAssistantSubject(figureSubject)
|
|
@@ -4,8 +4,9 @@ import { FigureReplacedDraft } from './figure-replaced-draft.js'
|
|
|
4
4
|
import { FigureProposalRecord } from './figure-proposal-record.js'
|
|
5
5
|
import { FigureQuery } from './figure-query.js'
|
|
6
6
|
import { FigureMutation } from './figure-mutation.js'
|
|
7
|
+
import { FigureNamespaceResolver } from './figure-namespace-resolver.js'
|
|
7
8
|
import { FigureHistoryEntitySubscriber } from './event-subscriber.js'
|
|
8
9
|
|
|
9
10
|
export const entities = [Figure, FigureHistory, FigureReplacedDraft, FigureProposalRecord]
|
|
10
|
-
export const resolvers = [FigureQuery, FigureMutation]
|
|
11
|
+
export const resolvers = [FigureQuery, FigureMutation, FigureNamespaceResolver]
|
|
11
12
|
export const subscribers = [FigureHistoryEntitySubscriber]
|
package/translations/en.json
CHANGED
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"figure.gate.origin-off-centre.how": "Keep it when the figure is fastened there, as a door at its hinge. Move the origin to the middle to place it by its room.",
|
|
101
101
|
"error.figure-namespace-missing": "this domain has no valid figure namespace ({value}). set {setting} first, for example hatiolab.com.",
|
|
102
102
|
"error.figure-namespace-changed": "the figure namespace of this domain is {namespace} and cannot change. set {setting} back to {namespace}.",
|
|
103
|
+
"error.figure-namespace-invalid": "{value} is not a figure namespace. use lower-case letters, digits, dots and hyphens, for example hatiolab.com.",
|
|
103
104
|
"error.figure-type-malformed": "{type} is not a figure type. use lower-case letters, digits and single hyphens, {min} to {max} characters, starting with a letter.",
|
|
104
105
|
"error.figure-name-malformed": "{type} is not a full figure name of the form namespace/type.",
|
|
105
106
|
"error.figure-not-released": "no release is published here.",
|
package/translations/es.json
CHANGED
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"figure.gate.origin-off-centre.how": "Manténgalo si la figura se fija en ese punto, como una puerta por su bisagra. Mueva el origen al centro para colocarla por su espacio.",
|
|
101
101
|
"error.figure-namespace-missing": "este dominio no tiene un namespace de figura válido ({value}). Defina primero {setting}, por ejemplo hatiolab.com.",
|
|
102
102
|
"error.figure-namespace-changed": "el namespace de figura de este dominio es {namespace} y no se puede cambiar. Restablezca {setting} a {namespace}.",
|
|
103
|
+
"error.figure-namespace-invalid": "{value} no es un namespace de Figure. Use minúsculas, dígitos, puntos y guiones, por ejemplo hatiolab.com.",
|
|
103
104
|
"error.figure-type-malformed": "{type} no es un tipo de figura. Use letras minúsculas, dígitos y guiones simples, de {min} a {max} caracteres, empezando por una letra.",
|
|
104
105
|
"error.figure-name-malformed": "{type} no es un nombre completo de figura con el formato namespace/type.",
|
|
105
106
|
"error.figure-not-released": "aquí no hay ninguna publicación.",
|
package/translations/ja.json
CHANGED
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"figure.gate.origin-off-centre.how": "ドアの蝶番のように、Figure をそこで取り付ける場合はそのままにしてください。空間の中央で配置するには、原点を中央に移動してください。",
|
|
101
101
|
"error.figure-namespace-missing": "このドメインには有効な Figure namespace がありません({value})。先に {setting} を設定してください。例: hatiolab.com。",
|
|
102
102
|
"error.figure-namespace-changed": "このドメインの Figure namespace は {namespace} で、変更できません。{setting} を {namespace} に戻してください。",
|
|
103
|
+
"error.figure-namespace-invalid": "Figure namespace の形式ではありません({value})。英小文字・数字・ドット・ハイフンで入力してください。例: hatiolab.com",
|
|
103
104
|
"error.figure-type-malformed": "{type} は Figure type ではありません。英小文字で始め、英小文字・数字・単一のハイフンのみを使って {min}〜{max} 文字で指定してください。",
|
|
104
105
|
"error.figure-name-malformed": "{type} は namespace/type 形式の正式な Figure 名ではありません。",
|
|
105
106
|
"error.figure-not-released": "ここではリリースが公開されていません。",
|
package/translations/ko.json
CHANGED
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"figure.gate.origin-off-centre.how": "문의 경첩처럼 붙이는 자리라면 그대로 둡니다. 공간 가운데로 놓이게 하려면 원점을 가운데로 옮깁니다.",
|
|
101
101
|
"error.figure-namespace-missing": "이 도메인에 Figure namespace 가 없거나 형식이 맞지 않습니다({value}). 설정 {setting} 에 예를 들어 hatiolab.com 을 먼저 넣어 주세요.",
|
|
102
102
|
"error.figure-namespace-changed": "이 도메인의 Figure namespace 는 {namespace} 이며 바꿀 수 없습니다. 설정 {setting} 을 {namespace} 로 되돌려 주세요.",
|
|
103
|
+
"error.figure-namespace-invalid": "Figure namespace 형식이 아닙니다({value}). 영문 소문자·숫자·점·하이픈으로 적어 주세요. 예: hatiolab.com",
|
|
103
104
|
"error.figure-type-malformed": "{type} 은 Figure type 이 아닙니다. 소문자로 시작하고 소문자·숫자·하이픈 하나씩만 써서 {min}~{max}자로 정해 주세요.",
|
|
104
105
|
"error.figure-name-malformed": "{type} 은 namespace/type 형식의 정식 이름이 아닙니다.",
|
|
105
106
|
"error.figure-not-released": "이 도메인에 발행판이 없습니다.",
|
package/translations/ms.json
CHANGED
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"figure.gate.origin-off-centre.how": "Kekalkan jika figure dilekatkan di situ, seperti pintu pada engselnya. Alihkan titik asal ke tengah untuk meletakkannya mengikut ruangnya.",
|
|
101
101
|
"error.figure-namespace-missing": "domain ini tiada namespace figure yang sah ({value}). tetapkan {setting} dahulu, contohnya hatiolab.com.",
|
|
102
102
|
"error.figure-namespace-changed": "namespace figure domain ini ialah {namespace} dan tidak boleh diubah. tetapkan {setting} kembali kepada {namespace}.",
|
|
103
|
+
"error.figure-namespace-invalid": "{value} bukan namespace Figure. Gunakan huruf kecil, digit, titik dan sempang, contohnya hatiolab.com.",
|
|
103
104
|
"error.figure-type-malformed": "{type} bukan jenis figure. gunakan huruf kecil, digit dan tanda sempang tunggal, {min} hingga {max} aksara, bermula dengan huruf.",
|
|
104
105
|
"error.figure-name-malformed": "{type} bukan nama figure penuh dalam bentuk namespace/type.",
|
|
105
106
|
"error.figure-not-released": "tiada terbitan diterbitkan di sini.",
|
package/translations/zh.json
CHANGED
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"figure.gate.origin-off-centre.how": "如果图形固定在该处(如门的铰链),请保持不变。若要按空间放置,请将原点移到中心。",
|
|
101
101
|
"error.figure-namespace-missing": "此域没有有效的图形 namespace({value})。请先设置 {setting},例如 hatiolab.com。",
|
|
102
102
|
"error.figure-namespace-changed": "此域的图形 namespace 为 {namespace},不能更改。请将 {setting} 改回 {namespace}。",
|
|
103
|
+
"error.figure-namespace-invalid": "不是 Figure namespace 的格式({value})。请使用小写字母、数字、点和连字符,例如 hatiolab.com。",
|
|
103
104
|
"error.figure-type-malformed": "{type} 不是图形类型。请使用小写字母、数字和单个连字符,长度 {min} 到 {max} 个字符,并以字母开头。",
|
|
104
105
|
"error.figure-name-malformed": "{type} 不是 namespace/type 形式的完整图形名称。",
|
|
105
106
|
"error.figure-not-released": "此处没有已发布的版本。",
|