@inlang/sdk 0.34.9 → 0.34.10
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/adapter/solidAdapter.js +1 -1
- package/dist/adapter/solidAdapter.test.js +60 -23
- package/dist/api.d.ts +15 -7
- package/dist/api.d.ts.map +1 -1
- package/dist/createMessageLintReportsQuery.d.ts.map +1 -1
- package/dist/createMessageLintReportsQuery.js +158 -64
- package/dist/createMessagesQuery.d.ts.map +1 -1
- package/dist/createMessagesQuery.js +29 -11
- package/dist/lint/message/lintSingleMessage.d.ts.map +1 -1
- package/dist/lint/message/lintSingleMessage.js +3 -1
- package/dist/loadProject.d.ts.map +1 -1
- package/dist/loadProject.js +6 -2
- package/dist/loadProject.test.js +38 -25
- package/dist/reactivity/solid.d.ts +2 -1
- package/dist/reactivity/solid.d.ts.map +1 -1
- package/dist/reactivity/solid.js +3 -2
- package/dist/reactivity/solid.test.js +38 -1
- package/dist/v2/createMessageBundle.d.ts +25 -0
- package/dist/v2/createMessageBundle.d.ts.map +1 -0
- package/dist/v2/createMessageBundle.js +36 -0
- package/dist/v2/createMessageBundle.test.d.ts +2 -0
- package/dist/v2/createMessageBundle.test.d.ts.map +1 -0
- package/dist/v2/createMessageBundle.test.js +92 -0
- package/dist/v2/mocks/plural/bundle.d.ts +3 -0
- package/dist/v2/mocks/plural/bundle.d.ts.map +1 -0
- package/dist/v2/mocks/plural/bundle.js +140 -0
- package/dist/v2/mocks/plural/bundle.test.d.ts +2 -0
- package/dist/v2/mocks/plural/bundle.test.d.ts.map +1 -0
- package/dist/v2/mocks/plural/bundle.test.js +15 -0
- package/package.json +5 -5
- package/src/adapter/solidAdapter.test.ts +78 -33
- package/src/adapter/solidAdapter.ts +1 -1
- package/src/api.ts +14 -7
- package/src/createMessageLintReportsQuery.ts +183 -68
- package/src/createMessagesQuery.ts +32 -11
- package/src/createNodeishFsWithWatcher.ts +1 -1
- package/src/lint/message/lintSingleMessage.ts +4 -1
- package/src/loadProject.test.ts +45 -24
- package/src/loadProject.ts +7 -2
- package/src/reactivity/solid.test.ts +54 -1
- package/src/reactivity/solid.ts +3 -0
- package/src/v2/createMessageBundle.test.ts +95 -0
- package/src/v2/createMessageBundle.ts +43 -0
- package/src/v2/mocks/plural/bundle.test.ts +18 -0
- package/src/v2/mocks/plural/bundle.ts +142 -0
package/src/loadProject.test.ts
CHANGED
|
@@ -635,8 +635,10 @@ describe("functionality", () => {
|
|
|
635
635
|
|
|
636
636
|
await new Promise((resolve) => setTimeout(resolve, 510))
|
|
637
637
|
|
|
638
|
-
expect(await project.query.messageLintReports.getAll()).toHaveLength(1)
|
|
639
|
-
expect((await project.query.messageLintReports.getAll())?.[0]?.ruleId).toBe(
|
|
638
|
+
expect(await project.query.messageLintReports.getAll.settled()).toHaveLength(1)
|
|
639
|
+
expect((await project.query.messageLintReports.getAll.settled())?.[0]?.ruleId).toBe(
|
|
640
|
+
_mockLintRule.id
|
|
641
|
+
)
|
|
640
642
|
expect(project.installed.messageLintRules()).toHaveLength(1)
|
|
641
643
|
})
|
|
642
644
|
|
|
@@ -1014,25 +1016,6 @@ describe("functionality", () => {
|
|
|
1014
1016
|
})
|
|
1015
1017
|
|
|
1016
1018
|
describe("lint", () => {
|
|
1017
|
-
it.todo("should throw if lint reports are not initialized yet", async () => {
|
|
1018
|
-
const repo = await mockRepo()
|
|
1019
|
-
const fs = repo.nodeishFs
|
|
1020
|
-
await fs.mkdir("/user/project", { recursive: true })
|
|
1021
|
-
await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings))
|
|
1022
|
-
const project = await loadProject({
|
|
1023
|
-
projectPath: "/user/project/project.inlang.json",
|
|
1024
|
-
repo,
|
|
1025
|
-
_import,
|
|
1026
|
-
})
|
|
1027
|
-
// TODO: test with real lint rules
|
|
1028
|
-
try {
|
|
1029
|
-
const r = await project.query.messageLintReports.getAll()
|
|
1030
|
-
expect(r).toEqual(undefined)
|
|
1031
|
-
throw new Error("Should not reach this")
|
|
1032
|
-
} catch (e) {
|
|
1033
|
-
expect((e as Error).message).toBe("lint not initialized yet")
|
|
1034
|
-
}
|
|
1035
|
-
})
|
|
1036
1019
|
it("should return the message lint reports", async () => {
|
|
1037
1020
|
const settings: ProjectSettings = {
|
|
1038
1021
|
sourceLanguageTag: "en",
|
|
@@ -1051,7 +1034,7 @@ describe("functionality", () => {
|
|
|
1051
1034
|
}),
|
|
1052
1035
|
})
|
|
1053
1036
|
// TODO: test with real lint rules
|
|
1054
|
-
const r = await project.query.messageLintReports.getAll()
|
|
1037
|
+
const r = await project.query.messageLintReports.getAll.settled()
|
|
1055
1038
|
expect(r).toEqual([])
|
|
1056
1039
|
})
|
|
1057
1040
|
})
|
|
@@ -1083,6 +1066,23 @@ describe("functionality", () => {
|
|
|
1083
1066
|
],
|
|
1084
1067
|
}
|
|
1085
1068
|
|
|
1069
|
+
const newMessage = {
|
|
1070
|
+
id: "test2",
|
|
1071
|
+
selectors: [],
|
|
1072
|
+
variants: [
|
|
1073
|
+
{
|
|
1074
|
+
match: [],
|
|
1075
|
+
languageTag: "en",
|
|
1076
|
+
pattern: [
|
|
1077
|
+
{
|
|
1078
|
+
type: "Text",
|
|
1079
|
+
value: "test",
|
|
1080
|
+
},
|
|
1081
|
+
],
|
|
1082
|
+
},
|
|
1083
|
+
],
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
1086
|
await fs.writeFile("./messages.json", JSON.stringify(messages))
|
|
1087
1087
|
|
|
1088
1088
|
const getMessages = async (customFs: NodeishFilesystemSubset) => {
|
|
@@ -1121,9 +1121,11 @@ describe("functionality", () => {
|
|
|
1121
1121
|
})
|
|
1122
1122
|
|
|
1123
1123
|
let counter = 0
|
|
1124
|
+
let messageCount = 0
|
|
1124
1125
|
|
|
1125
|
-
project.query.messages.getAll.subscribe(() => {
|
|
1126
|
+
project.query.messages.getAll.subscribe((messages) => {
|
|
1126
1127
|
counter = counter + 1
|
|
1128
|
+
messageCount = messages.length
|
|
1127
1129
|
})
|
|
1128
1130
|
|
|
1129
1131
|
// subscribe fires once
|
|
@@ -1135,6 +1137,7 @@ describe("functionality", () => {
|
|
|
1135
1137
|
|
|
1136
1138
|
// we didn't change the message we write into message.json - shouldn't change the messages
|
|
1137
1139
|
expect(counter).toBe(1)
|
|
1140
|
+
expect(messageCount).toBe(1)
|
|
1138
1141
|
|
|
1139
1142
|
// saving the file without changing should trigger a change
|
|
1140
1143
|
messages.data[0]!.variants[0]!.pattern[0]!.value = "changed"
|
|
@@ -1142,14 +1145,32 @@ describe("functionality", () => {
|
|
|
1142
1145
|
await new Promise((resolve) => setTimeout(resolve, 200)) // file event will lock a file and be handled sequentially - give it time to pickup the change
|
|
1143
1146
|
|
|
1144
1147
|
expect(counter).toBe(2)
|
|
1148
|
+
expect(messageCount).toBe(1)
|
|
1145
1149
|
|
|
1146
1150
|
messages.data[0]!.variants[0]!.pattern[0]!.value = "changed3"
|
|
1147
1151
|
|
|
1148
|
-
// change file
|
|
1152
|
+
// change file - update message
|
|
1149
1153
|
await fs.writeFile("./messages.json", JSON.stringify(messages))
|
|
1150
1154
|
await new Promise((resolve) => setTimeout(resolve, 200)) // file event will lock a file and be handled sequentially - give it time to pickup the change
|
|
1151
1155
|
|
|
1152
1156
|
expect(counter).toBe(3)
|
|
1157
|
+
expect(messageCount).toBe(1)
|
|
1158
|
+
|
|
1159
|
+
// change file - add a message
|
|
1160
|
+
messages.data.push(newMessage)
|
|
1161
|
+
await fs.writeFile("./messages.json", JSON.stringify(messages))
|
|
1162
|
+
await new Promise((resolve) => setTimeout(resolve, 200)) // file event will lock a file and be handled sequentially - give it time to pickup the change
|
|
1163
|
+
|
|
1164
|
+
expect(counter).toBe(4)
|
|
1165
|
+
expect(messageCount).toBe(2)
|
|
1166
|
+
|
|
1167
|
+
// change file - remove a message
|
|
1168
|
+
messages.data.pop()
|
|
1169
|
+
await fs.writeFile("./messages.json", JSON.stringify(messages))
|
|
1170
|
+
await new Promise((resolve) => setTimeout(resolve, 200)) // file event will lock a file and be handled sequentially - give it time to pickup the change
|
|
1171
|
+
|
|
1172
|
+
expect(counter).toBe(5)
|
|
1173
|
+
expect(messageCount).toBe(1)
|
|
1153
1174
|
})
|
|
1154
1175
|
})
|
|
1155
1176
|
})
|
package/src/loadProject.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
ProjectSettingsFileNotFoundError,
|
|
13
13
|
ProjectSettingsInvalidError,
|
|
14
14
|
} from "./errors.js"
|
|
15
|
-
import { createRoot, createSignal, createEffect } from "./reactivity/solid.js"
|
|
15
|
+
import { createRoot, createSignal, createEffect, batch } from "./reactivity/solid.js"
|
|
16
16
|
import { createMessagesQuery } from "./createMessagesQuery.js"
|
|
17
17
|
import { createMessageLintReportsQuery } from "./createMessageLintReportsQuery.js"
|
|
18
18
|
import { ProjectSettings, type NodeishFilesystemSubset } from "./versionedInterfaces.js"
|
|
@@ -111,7 +111,12 @@ export async function loadProject(args: {
|
|
|
111
111
|
pathPattern: projectPath + "/messages.json",
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
-
|
|
114
|
+
|
|
115
|
+
batch(() => {
|
|
116
|
+
// reset the resolved modules first - since they are no longer valid at that point
|
|
117
|
+
setResolvedModules(undefined)
|
|
118
|
+
_setSettings(validatedSettings)
|
|
119
|
+
})
|
|
115
120
|
|
|
116
121
|
writeSettingsToDisk(validatedSettings)
|
|
117
122
|
return { data: undefined }
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest"
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
createRoot,
|
|
4
|
+
createSignal,
|
|
5
|
+
createEffect,
|
|
6
|
+
createMemo,
|
|
7
|
+
createResource,
|
|
8
|
+
untrack,
|
|
9
|
+
} from "./solid.js"
|
|
10
|
+
import { ReactiveMap } from "./map.js"
|
|
3
11
|
|
|
4
12
|
function sleep(ms: number) {
|
|
5
13
|
return new Promise((resolve) => setTimeout(resolve, ms))
|
|
@@ -174,3 +182,48 @@ describe("solid", () => {
|
|
|
174
182
|
expect(memo()).toBe("memo = 2000")
|
|
175
183
|
})
|
|
176
184
|
})
|
|
185
|
+
|
|
186
|
+
describe("solid", () => {
|
|
187
|
+
it("solid reactive map allows to use untrack", () => {
|
|
188
|
+
// @ts-expect-error -- ReactiveMap seem to have problems type arguments here
|
|
189
|
+
const reactiveMap = new ReactiveMap<string, any>()
|
|
190
|
+
const [plainSignal, setPlainSignal] = createSignal(0)
|
|
191
|
+
|
|
192
|
+
let shouldTriggerOnBoth = -1
|
|
193
|
+
createEffect(() => {
|
|
194
|
+
shouldTriggerOnBoth++
|
|
195
|
+
reactiveMap.values()
|
|
196
|
+
plainSignal()
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
let shouldTriggerOnReactiveMapOnly = -1
|
|
200
|
+
createEffect(() => {
|
|
201
|
+
shouldTriggerOnReactiveMapOnly++
|
|
202
|
+
reactiveMap.values()
|
|
203
|
+
untrack(() => plainSignal())
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
let shouldTriggerOnPlainSignalOnly = -1
|
|
207
|
+
createEffect(() => {
|
|
208
|
+
shouldTriggerOnPlainSignalOnly++
|
|
209
|
+
untrack(() => reactiveMap.values())
|
|
210
|
+
plainSignal()
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
expect(shouldTriggerOnBoth).toBe(0)
|
|
214
|
+
expect(shouldTriggerOnReactiveMapOnly).toBe(0)
|
|
215
|
+
expect(shouldTriggerOnPlainSignalOnly).toBe(0)
|
|
216
|
+
|
|
217
|
+
setPlainSignal(1)
|
|
218
|
+
|
|
219
|
+
expect(shouldTriggerOnBoth).toBe(1)
|
|
220
|
+
expect(shouldTriggerOnReactiveMapOnly).toBe(0)
|
|
221
|
+
expect(shouldTriggerOnPlainSignalOnly).toBe(1)
|
|
222
|
+
|
|
223
|
+
reactiveMap.set("a", "a")
|
|
224
|
+
|
|
225
|
+
expect(shouldTriggerOnBoth).toBe(2)
|
|
226
|
+
expect(shouldTriggerOnReactiveMapOnly).toBe(1)
|
|
227
|
+
expect(shouldTriggerOnPlainSignalOnly).toBe(1)
|
|
228
|
+
})
|
|
229
|
+
})
|
package/src/reactivity/solid.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
createRoot as _createRoot,
|
|
5
5
|
createEffect as _createEffect,
|
|
6
6
|
createResource as _createResource,
|
|
7
|
+
untrack as _untrack,
|
|
7
8
|
observable as _observable,
|
|
8
9
|
batch as _batch,
|
|
9
10
|
from as _from,
|
|
@@ -23,6 +24,7 @@ const from = _from as typeof import("solid-js")["from"]
|
|
|
23
24
|
const batch = _batch as typeof import("solid-js")["batch"]
|
|
24
25
|
const getListener = _getListener as typeof import("solid-js")["getListener"]
|
|
25
26
|
const onCleanup = _onCleanup as typeof import("solid-js")["onCleanup"]
|
|
27
|
+
const untrack = _untrack as typeof import("solid-js")["untrack"]
|
|
26
28
|
|
|
27
29
|
export {
|
|
28
30
|
createSignal,
|
|
@@ -35,5 +37,6 @@ export {
|
|
|
35
37
|
batch,
|
|
36
38
|
getListener,
|
|
37
39
|
onCleanup,
|
|
40
|
+
untrack,
|
|
38
41
|
DEV,
|
|
39
42
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest"
|
|
2
|
+
import { createMessageBundle, createMessage } from "./createMessageBundle.js"
|
|
3
|
+
import { MessageBundle } from "./types.js"
|
|
4
|
+
import { Value } from "@sinclair/typebox/value"
|
|
5
|
+
|
|
6
|
+
describe("createMessageBundle", () => {
|
|
7
|
+
it("creates a bundle with no messages", () => {
|
|
8
|
+
const bundle: unknown = createMessageBundle({ id: "no_messages", messages: [] })
|
|
9
|
+
expect(Value.Check(MessageBundle, bundle)).toBe(true)
|
|
10
|
+
expect(bundle).toEqual({
|
|
11
|
+
id: "no_messages",
|
|
12
|
+
alias: {},
|
|
13
|
+
messages: [],
|
|
14
|
+
} satisfies MessageBundle)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it("creates a bundle with a single text-only message", () => {
|
|
18
|
+
const bundle: unknown = createMessageBundle({
|
|
19
|
+
id: "hello_world",
|
|
20
|
+
messages: [createMessage({ locale: "en", text: "Hello World!" })],
|
|
21
|
+
})
|
|
22
|
+
expect(Value.Check(MessageBundle, bundle)).toBe(true)
|
|
23
|
+
expect(bundle).toEqual({
|
|
24
|
+
id: "hello_world",
|
|
25
|
+
alias: {},
|
|
26
|
+
messages: [
|
|
27
|
+
{
|
|
28
|
+
locale: "en",
|
|
29
|
+
declarations: [],
|
|
30
|
+
selectors: [],
|
|
31
|
+
variants: [
|
|
32
|
+
{
|
|
33
|
+
match: [],
|
|
34
|
+
pattern: [
|
|
35
|
+
{
|
|
36
|
+
type: "text",
|
|
37
|
+
value: "Hello World!",
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
} satisfies MessageBundle)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it("creates a bundle with multiple text-only messages", () => {
|
|
48
|
+
const bundle: unknown = createMessageBundle({
|
|
49
|
+
id: "hello_world_2",
|
|
50
|
+
messages: [
|
|
51
|
+
createMessage({ locale: "en", text: "Hello World!" }),
|
|
52
|
+
createMessage({ locale: "de", text: "Hallo Welt!" }),
|
|
53
|
+
],
|
|
54
|
+
})
|
|
55
|
+
expect(Value.Check(MessageBundle, bundle)).toBe(true)
|
|
56
|
+
expect(bundle).toEqual({
|
|
57
|
+
id: "hello_world_2",
|
|
58
|
+
alias: {},
|
|
59
|
+
messages: [
|
|
60
|
+
{
|
|
61
|
+
locale: "en",
|
|
62
|
+
declarations: [],
|
|
63
|
+
selectors: [],
|
|
64
|
+
variants: [
|
|
65
|
+
{
|
|
66
|
+
match: [],
|
|
67
|
+
pattern: [
|
|
68
|
+
{
|
|
69
|
+
type: "text",
|
|
70
|
+
value: "Hello World!",
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
locale: "de",
|
|
78
|
+
declarations: [],
|
|
79
|
+
selectors: [],
|
|
80
|
+
variants: [
|
|
81
|
+
{
|
|
82
|
+
match: [],
|
|
83
|
+
pattern: [
|
|
84
|
+
{
|
|
85
|
+
type: "text",
|
|
86
|
+
value: "Hallo Welt!",
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
} satisfies MessageBundle)
|
|
94
|
+
})
|
|
95
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { LanguageTag, MessageBundle, Message, Text } from "./types.js"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* create v2 MessageBundle
|
|
5
|
+
* @example createMessageBundle({
|
|
6
|
+
* id: "greeting",
|
|
7
|
+
* messages: [
|
|
8
|
+
* createMessage({locale: "en", text: "Hello world!"})
|
|
9
|
+
* createMessage({locale: "de", text: "Hallo Welt!"})
|
|
10
|
+
* ]
|
|
11
|
+
* })
|
|
12
|
+
*/
|
|
13
|
+
export function createMessageBundle(args: {
|
|
14
|
+
id: string
|
|
15
|
+
messages: Message[]
|
|
16
|
+
alias?: MessageBundle["alias"]
|
|
17
|
+
}): MessageBundle {
|
|
18
|
+
return {
|
|
19
|
+
id: args.id,
|
|
20
|
+
alias: args.alias ?? {},
|
|
21
|
+
messages: args.messages,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* create v2 Messsage AST with text-only pattern
|
|
27
|
+
* @example createMessage({locale: "en", text: "Hello world"})
|
|
28
|
+
*/
|
|
29
|
+
export function createMessage(args: { locale: LanguageTag; text: string }): Message {
|
|
30
|
+
return {
|
|
31
|
+
locale: args.locale,
|
|
32
|
+
declarations: [],
|
|
33
|
+
selectors: [],
|
|
34
|
+
variants: [{ match: [], pattern: [toTextElement(args.text ?? "")] }],
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function toTextElement(text: string): Text {
|
|
39
|
+
return {
|
|
40
|
+
type: "text",
|
|
41
|
+
value: text,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2
|
+
|
|
3
|
+
import { describe, it, expect } from "vitest"
|
|
4
|
+
import { bundle } from "./bundle.js"
|
|
5
|
+
import { MessageBundle } from "../../types.js"
|
|
6
|
+
import { Value } from "@sinclair/typebox/value"
|
|
7
|
+
|
|
8
|
+
describe("mock plural messageBundle", () => {
|
|
9
|
+
it("is valid", () => {
|
|
10
|
+
const messageBundle: unknown = bundle
|
|
11
|
+
expect(Value.Check(MessageBundle, messageBundle)).toBe(true)
|
|
12
|
+
|
|
13
|
+
expect(bundle.messages.length).toBe(2)
|
|
14
|
+
expect(bundle.messages[0]!.declarations.length).toBe(1)
|
|
15
|
+
expect(bundle.messages[0]!.selectors.length).toBe(1)
|
|
16
|
+
expect(bundle.messages[0]!.variants.length).toBe(3)
|
|
17
|
+
})
|
|
18
|
+
})
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { MessageBundle } from "../../types.js"
|
|
2
|
+
|
|
3
|
+
export const bundle: MessageBundle = {
|
|
4
|
+
id: "mock_bundle_human_id",
|
|
5
|
+
alias: {
|
|
6
|
+
default: "mock_bundle_alias",
|
|
7
|
+
},
|
|
8
|
+
messages: [
|
|
9
|
+
{
|
|
10
|
+
locale: "de",
|
|
11
|
+
declarations: [
|
|
12
|
+
{
|
|
13
|
+
type: "input",
|
|
14
|
+
name: "numProducts",
|
|
15
|
+
value: {
|
|
16
|
+
type: "expression",
|
|
17
|
+
arg: {
|
|
18
|
+
type: "variable",
|
|
19
|
+
name: "numProducts",
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
selectors: [
|
|
25
|
+
{
|
|
26
|
+
type: "expression",
|
|
27
|
+
arg: {
|
|
28
|
+
type: "variable",
|
|
29
|
+
name: "numProducts",
|
|
30
|
+
},
|
|
31
|
+
annotation: {
|
|
32
|
+
type: "function",
|
|
33
|
+
name: "plural",
|
|
34
|
+
options: [],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
variants: [
|
|
39
|
+
{
|
|
40
|
+
match: ["zero"],
|
|
41
|
+
pattern: [
|
|
42
|
+
{
|
|
43
|
+
type: "text",
|
|
44
|
+
value: "Keine Produkte",
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
match: ["one"],
|
|
50
|
+
pattern: [
|
|
51
|
+
{
|
|
52
|
+
type: "text",
|
|
53
|
+
value: "Ein Produkt",
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
match: ["other"],
|
|
59
|
+
pattern: [
|
|
60
|
+
{
|
|
61
|
+
type: "expression",
|
|
62
|
+
arg: {
|
|
63
|
+
type: "variable",
|
|
64
|
+
name: "numProducts",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: "text",
|
|
69
|
+
value: " Produkte",
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
locale: "en",
|
|
77
|
+
declarations: [
|
|
78
|
+
{
|
|
79
|
+
type: "input",
|
|
80
|
+
name: "numProducts",
|
|
81
|
+
value: {
|
|
82
|
+
type: "expression",
|
|
83
|
+
arg: {
|
|
84
|
+
type: "variable",
|
|
85
|
+
name: "numProducts",
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
selectors: [
|
|
91
|
+
{
|
|
92
|
+
type: "expression",
|
|
93
|
+
arg: {
|
|
94
|
+
type: "variable",
|
|
95
|
+
name: "numProducts",
|
|
96
|
+
},
|
|
97
|
+
annotation: {
|
|
98
|
+
type: "function",
|
|
99
|
+
name: "plural",
|
|
100
|
+
options: [],
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
variants: [
|
|
105
|
+
{
|
|
106
|
+
match: ["zero"],
|
|
107
|
+
pattern: [
|
|
108
|
+
{
|
|
109
|
+
type: "text",
|
|
110
|
+
value: "No Products",
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
match: ["one"],
|
|
116
|
+
pattern: [
|
|
117
|
+
{
|
|
118
|
+
type: "text",
|
|
119
|
+
value: "A product",
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
match: ["other"],
|
|
125
|
+
pattern: [
|
|
126
|
+
{
|
|
127
|
+
type: "expression",
|
|
128
|
+
arg: {
|
|
129
|
+
type: "variable",
|
|
130
|
+
name: "numProducts",
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
type: "text",
|
|
135
|
+
value: " products",
|
|
136
|
+
},
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
}
|