@sveltekit-i18n/parser-curly 3.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 sveltekit-i18n
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,425 @@
1
+ [![npm version](https://badge.fury.io/js/@sveltekit-i18n%2Fparser-curly.svg)](https://badge.fury.io/js/@sveltekit-i18n%2Fparser-curly) [![Tests](https://github.com/sveltekit-i18n/parsers/actions/workflows/tests-parser-curly.yml/badge.svg)](https://github.com/sveltekit-i18n/parsers/actions/workflows/tests-parser-curly.yml)
2
+ [![Netlify Status](https://api.netlify.com/api/v1/badges/61a65082-1dc8-4c2a-94f2-0334c005dad0/deploy-status)](https://app.netlify.com/sites/parser-default/deploys)
3
+
4
+ # @sveltekit-i18n/parser-curly
5
+
6
+ The [Curly Message Format](https://github.com/curly-message/spec) for [@sveltekit-i18n/base](https://github.com/sveltekit-i18n/base): placeholders, defaults, modifiers and comparisons written in double curly braces. Every message is resolved by [`@curly-message/parser`](https://github.com/curly-message/parsers), the format's reference implementation and this package's only dependency; the package itself unpacks the base library's calling convention and supplies a default diagnostics channel. This README is a practical guide to the syntax — the full grammar and the resolution rules are in the specification repository.
7
+
8
+ **[Live Demo](https://parser-default.netlify.app)** – See it in action
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ npm install @sveltekit-i18n/parser-curly
14
+ ```
15
+
16
+ This parser is included by default in [sveltekit-i18n](https://github.com/sveltekit-i18n/lib).
17
+
18
+ **Requirements:** Node.js 22 or newer. Version 3 is ESM-only and expects [`@sveltekit-i18n/base`](https://github.com/sveltekit-i18n/base) v3 as a peer dependency.
19
+
20
+ ## Usage
21
+
22
+ ### With @sveltekit-i18n/base
23
+
24
+ ```javascript
25
+ import { I18n } from '@sveltekit-i18n/base';
26
+ import parser from '@sveltekit-i18n/parser-curly';
27
+
28
+ const config = {
29
+ parser: parser({
30
+ // Where diagnostics go; `null` states that they go nowhere.
31
+ onReport: null,
32
+ }),
33
+ loaders: [
34
+ {
35
+ locale: 'en',
36
+ key: 'common',
37
+ loader: async () => (await import('./en/common.json')).default,
38
+ },
39
+ ],
40
+ };
41
+
42
+ export const i18n = new I18n(config);
43
+ ```
44
+
45
+ ### With sveltekit-i18n
46
+
47
+ ```javascript
48
+ import { I18n } from 'sveltekit-i18n';
49
+
50
+ const config = {
51
+ // parser-curly is already included
52
+ loaders: [/* ... */],
53
+ };
54
+
55
+ export const i18n = new I18n(config);
56
+ ```
57
+
58
+ Either way, `i18n.t(key, payload?, props?)` takes the values the placeholders name and the per-call formatting options; the examples below use it.
59
+
60
+ ## Syntax
61
+
62
+ A placeholder names a payload key and may carry a modifier, options and a default: `{{key:modifier; optionKey:value; default:fallback;}}`. Whitespace around the key, the modifier name, the option keys and the option values is not significant, so `{{value}}`, `{{ value }}` and `{{ value; }}` are the same placeholder.
63
+
64
+ ### Placeholders
65
+
66
+ ```json
67
+ {
68
+ "greeting": "Hello, {{name}}!",
69
+ "message": "You have {{count}} new messages."
70
+ }
71
+ ```
72
+
73
+ ```javascript
74
+ i18n.t('greeting', { name: 'Alice' })
75
+ // → "Hello, Alice!"
76
+
77
+ i18n.t('message', { count: 5 })
78
+ // → "You have 5 new messages."
79
+ ```
80
+
81
+ Every value reaches the output as text: a plain object or an array becomes JSON, anything else becomes what `String()` makes of it.
82
+
83
+ ```javascript
84
+ i18n.t('greeting', { name: { first: 'Ann' } })
85
+ // → "Hello, {"first":"Ann"}!"
86
+
87
+ i18n.t('greeting', { name: ['Ann', 'Bob'] })
88
+ // → "Hello, ["Ann","Bob"]!"
89
+ ```
90
+
91
+ ### Default Values
92
+
93
+ ```json
94
+ {
95
+ "welcome": "Welcome, {{name; default:Guest;}}!"
96
+ }
97
+ ```
98
+
99
+ ```javascript
100
+ i18n.t('welcome', { name: 'Bob' })
101
+ // → "Welcome, Bob!"
102
+
103
+ i18n.t('welcome', {})
104
+ // → "Welcome, Guest!"
105
+
106
+ i18n.t('welcome', { default: 'Anonymous' })
107
+ // → "Welcome, Anonymous!"
108
+ ```
109
+
110
+ `default` is a reserved payload key: the fallback for every placeholder in the message whose value is absent, and `{{default}}` reads it. A placeholder with no value takes the first of these that yields text — the entry's own `default` (see [Payload](#payload)), the payload's `default`, the inline `default:`, the empty string — so the payload's `default` outranks the inline one. Only an absent value falls back: `0`, `false` and the empty string are values. A key that names no message resolves to the payload's `default` as well and, where the payload carries none, to the key itself, echoed verbatim and never read as a message.
111
+
112
+ ### Modifiers
113
+
114
+ A modifier follows the key after a colon. The formatting modifiers delegate to `Intl` and read their options from the props argument, keyed by the modifier's name — `{ date: { dateStyle: 'full' } }`:
115
+
116
+ | Modifier | Reads the value as | Options |
117
+ | --- | --- | --- |
118
+ | `number` | a number | `Intl.NumberFormat` options; at most two fraction digits unless a layer names `maximumFractionDigits`, or a `minimumFractionDigits` above two widens that default |
119
+ | `date` | milliseconds since the epoch, or text `Date` parses | `Intl.DateTimeFormat` options |
120
+ | `ago` | a signed millisecond delta from now, negative for the past | `Intl.RelativeTimeFormat` options, plus `format`: a unit from `second` to `year`, or `auto` |
121
+ | `currency` | a number, multiplied by `ratio` (default `1`) | `Intl.NumberFormat` options in the currency style; `currency` names the code |
122
+
123
+ ```json
124
+ {
125
+ "price": "Total: {{amount:number;}}",
126
+ "published": "Published: {{date:date;}}",
127
+ "time": "Time: {{timestamp:date;}}",
128
+ "updated": "Updated {{time:ago;}}",
129
+ "posted": "Posted {{timestamp:ago;}}",
130
+ "cost": "Cost: {{amount:currency;}}"
131
+ }
132
+ ```
133
+
134
+ ```javascript
135
+ i18n.t('price', { amount: 1234.56 })
136
+ // → "Total: 1,234.56" (locale-dependent)
137
+
138
+ i18n.t('price', { amount: 1234.56 }, { number: { maximumFractionDigits: 1 } })
139
+ // → "Total: 1,234.6"
140
+
141
+ i18n.t('published', { date: new Date(2024, 0, 1) }, { date: { dateStyle: 'full' } })
142
+ // → "Published: Monday, January 1, 2024"
143
+
144
+ i18n.t('time', { timestamp: new Date(2024, 0, 1, 10, 30) }, { date: { timeStyle: 'short' } })
145
+ // → "Time: 10:30 AM"
146
+
147
+ i18n.t('updated', { time: -3600000 })
148
+ // → "Updated 1 hour ago"
149
+
150
+ i18n.t('posted', { timestamp: -86400000 })
151
+ // → "Posted yesterday"
152
+
153
+ i18n.t('posted', { timestamp: -172800000 }, { ago: { format: 'hour' } })
154
+ // → "Posted 48 hours ago"
155
+
156
+ i18n.t('cost', { amount: 99.99 }, { currency: { currency: 'USD' } })
157
+ // → "Cost: $99.99"
158
+
159
+ i18n.t('cost', { amount: 1999 }, { currency: { currency: 'USD', ratio: 0.01 } })
160
+ // → "Cost: $19.99"
161
+ ```
162
+
163
+ A value the modifier cannot read — text that is not a number, an empty string, a `Date` object under `number` — takes the fallback and is reported as `failed-modifier`; so does a `currency` placeholder with no currency code or an `ago` whose `format` names no unit. Nothing raises. A `Date` object does work under `date`, to the second, because it reaches the modifier as its `toString` text; pass a timestamp or an ISO string where a placeholder wants one. With no locale — none passed, or the empty string — a formatting modifier resolves to the empty string rather than to the fallback and reports `missing-locale`.
164
+
165
+ ### Comparisons
166
+
167
+ `eq`, `ne`, `lt`, `lte`, `gt` and `gte` select among the options: the first option whose key satisfies the comparison against the value is the result, and none selected takes the fallback. `eq` and `ne` compare as text, case-insensitively; `lt` and `gt` compare numerically, considering the options in ascending or descending key order; `lte` and `gte` try equality first. A placeholder with options and no modifier compares with `eq`.
168
+
169
+ ```json
170
+ {
171
+ "status": "{{state; active:Online; inactive:Offline; default:Unknown;}}",
172
+ "items": "You have {{count}} {{count; 1:item; default:items;}}.",
173
+ "stock": "{{count:gt; 0:In stock ({{count}}); default:Out of stock;}}",
174
+ "age": "{{age:gte; 18:Adult; default:Minor;}}",
175
+ "temp": "{{degrees:lt; 0:Freezing; default:Above freezing;}}",
176
+ "health": "{{state:ne; ok:Problem; default:Fine;}}"
177
+ }
178
+ ```
179
+
180
+ ```javascript
181
+ i18n.t('status', { state: 'active' }) // → "Online"
182
+ i18n.t('status', { state: 'pending' }) // → "Unknown"
183
+ i18n.t('items', { count: 1 }) // → "You have 1 item."
184
+ i18n.t('items', { count: 5 }) // → "You have 5 items."
185
+ i18n.t('stock', { count: 5 }) // → "In stock (5)"
186
+ i18n.t('stock', { count: 0 }) // → "Out of stock"
187
+ i18n.t('age', { age: 25 }) // → "Adult"
188
+ i18n.t('temp', { degrees: -5 }) // → "Freezing"
189
+ i18n.t('health', { state: 'error' }) // → "Problem"
190
+ i18n.t('health', {}) // → "Fine"
191
+ ```
192
+
193
+ An absent value never reaches a comparison — it takes the fallback under every modifier, `ne` included. An option written as `key` alone stands for its own key; `key:` declares the empty string. An option value runs to the next unescaped semicolon, so `link:http://example.com` keeps its colons. A comparison with no options (`{{v:eq; default:D}}`) takes the fallback and reports `missing-options`; a placeholder naming a modifier nobody registered takes the fallback and reports `unknown-modifier` — it is never run as `eq`.
194
+
195
+ ### Nested Placeholders
196
+
197
+ ```json
198
+ {
199
+ "notification": "You have {{count:gt; 0:{{count}} new {{count; 1:message; default:messages;}}!; default:no messages.;}}"
200
+ }
201
+ ```
202
+
203
+ ```javascript
204
+ i18n.t('notification', { count: 1 })
205
+ // → "You have 1 new message!"
206
+
207
+ i18n.t('notification', { count: 5 })
208
+ // → "You have 5 new messages!"
209
+
210
+ i18n.t('notification', { count: 0 })
211
+ // → "You have no messages."
212
+ ```
213
+
214
+ Nesting is resolved by interpolating the output again, so a payload value may carry a placeholder of its own; the [limits](#limits) bound that.
215
+
216
+ ### Escaping
217
+
218
+ A backslash cancels the structural meaning of the character after it: `:`, `;`, `{`, `}`, whitespace and the backslash itself. Before any other character the backslash is text, so a regular expression or a Windows path survives as typed. In a JSON catalogue each backslash is written twice (`"\\{\\{"`).
219
+
220
+ | Message | Result |
221
+ | --- | --- |
222
+ | `Braces are written \{\{ like this \}\}` | `Braces are written {{ like this }}` |
223
+ | `Ratio\: 3\:1\;` | `Ratio: 3:1;` |
224
+ | `Hello, {{first\ name}}!` | reads the payload key `first name` |
225
+ | `{{count; 1:one\ ; default:none}}` | keeps the trailing space the trimming would take |
226
+ | `C:\\temp` | `C:\temp` |
227
+ | `\d+` | `\d+` |
228
+ | `\{{v}}` | `{{v}}`, text whatever the payload carries |
229
+
230
+ Escape sequences are removed once, from the finished text, and a payload value is read by the same rule: a value that must keep a backslash before a reserved character doubles it. A placeholder is written on one line — `{{` and `}}` with a line terminator between them are text — and `{{}}` is a placeholder naming no key, which resolves to the fallback.
231
+
232
+ ## Payload
233
+
234
+ A payload entry may be a wrapper instead of the value: a plain object owning at least one of `value`, `default` and `props` and nothing else. Its `default` is tried before the payload's, and its `props` are the topmost formatting layer.
235
+
236
+ ```javascript
237
+ i18n.t('price', { amount: { value: 1234.56, props: { number: { maximumFractionDigits: 1 } } } })
238
+ // → "Total: 1,234.6"
239
+
240
+ i18n.t('greeting', { name: { default: 'stranger' } })
241
+ // → "Hello, stranger!"
242
+ ```
243
+
244
+ An entry owning any other key is data, wrapper-shaped or not: `{ value: 1, unit: 'kg' }` becomes JSON. Every entry is read as an own enumerable property — nothing on a prototype resolves.
245
+
246
+ ## Options
247
+
248
+ ```javascript
249
+ import parser from '@sveltekit-i18n/parser-curly';
250
+
251
+ const config = {
252
+ parser: parser({
253
+ // The bottom formatting layer, keyed by modifier name. The props a call
254
+ // passes and a wrapper's own props layer over it, property by property.
255
+ modifierDefaults: {
256
+ number: { minimumFractionDigits: 2, maximumFractionDigits: 2 },
257
+ date: { dateStyle: 'medium' },
258
+ ago: { numeric: 'auto' },
259
+ currency: { currency: 'USD' },
260
+ },
261
+ // Modifiers by name, over the built-in ones.
262
+ customModifiers: {},
263
+ // Where diagnostics go. Required: a function, or `null` to state that
264
+ // reports go nowhere.
265
+ onReport: (report) => { /* ... */ },
266
+ }),
267
+ };
268
+ ```
269
+
270
+ The formatting layers compose per property, so a layer overrides only what it names:
271
+
272
+ ```
273
+ modifierDefaults { number: { maximumFractionDigits: 4, useGrouping: false } }
274
+ call props { number: { useGrouping: true } }
275
+ wrapper props { number: { maximumFractionDigits: 1 } }
276
+ effective { maximumFractionDigits: 1, useGrouping: true } → "1,234.6"
277
+ ```
278
+
279
+ ### Custom Modifiers
280
+
281
+ A modifier is a function of `{ value, options, defaultValue, props, locale }`: the value as text, the options as `[{ key, value }]` in source order, the fallback chain behind `defaultValue` (resolved when read), the props composed under the modifier's own name (an empty object where nothing is configured) and the locale. What it returns becomes text. A modifier that returns nothing leaves the placeholder to its fallback, and so does one that throws, which is reported as `failed-modifier`. An absent value takes the fallback before any modifier runs. A modifier registered under a built-in name replaces it.
282
+
283
+ ```javascript
284
+ const config = {
285
+ parser: parser({
286
+ customModifiers: {
287
+ // Absolute value equality
288
+ eqAbs: ({ value, options, defaultValue }) =>
289
+ options.find(({ key }) => Math.abs(+key) === Math.abs(+value))?.value ?? defaultValue,
290
+
291
+ // Uppercase transform
292
+ upper: ({ value }) => value.toUpperCase(),
293
+
294
+ // Truncate text; `props` is what the call passes under `truncate`
295
+ truncate: ({ value, props }) => {
296
+ const maxLength = props.maxLength ?? 50;
297
+
298
+ return value.length > maxLength ? `${value.slice(0, maxLength)}...` : value;
299
+ },
300
+ },
301
+ }),
302
+ };
303
+ ```
304
+
305
+ ```json
306
+ {
307
+ "score": "{{value:eqAbs; 10:Perfect score!; default:Not quite.;}}",
308
+ "title": "{{text:upper;}}",
309
+ "description": "{{text:truncate;}}"
310
+ }
311
+ ```
312
+
313
+ ```javascript
314
+ i18n.t('score', { value: -10 })
315
+ // → "Perfect score!"
316
+
317
+ i18n.t('title', { text: 'hello world' })
318
+ // → "HELLO WORLD"
319
+
320
+ i18n.t('description', { text: 'This text is far too long to display' }, { truncate: { maxLength: 20 } })
321
+ // → "This text is far too..."
322
+ ```
323
+
324
+ ### Reports
325
+
326
+ The format prescribes no diagnostics channel, and neither does this package: `@curly-message/parser` writes nowhere by itself, and `parser()` adds no writer of its own. `onReport` is therefore a required option — a function, or `null` to state that reports go nowhere — so that silence is a decision, never an omission. A host with a logger routes reports to it:
327
+
328
+ ```javascript
329
+ parser({ onReport: (report) => logger.warn(report.message, report) })
330
+ ```
331
+
332
+ A `Report` carries:
333
+
334
+ | Field | Meaning |
335
+ | --- | --- |
336
+ | `code` | `unknown-modifier`, `failed-modifier`, `missing-options`, `unserializable-value`, `missing-locale`, `pass-limit` or `output-limit` |
337
+ | `origin` | who fixes it: `message` (the message as written), `payload` (what the call passed) or `limit` (a bound this parser set) |
338
+ | `message` | a self-contained English sentence carrying nothing from the payload |
339
+ | `key` | the message's key, where the call passed one |
340
+ | `limit` | the limit reached, for the two limit reports |
341
+ | `text` | the excerpt: the placeholder, or the output that would not settle — cut to 120 code units, with quotes, backslashes and line terminators escaped, so it can be written anywhere |
342
+
343
+ A report never raises: the placeholder takes its fallback (the empty string for `missing-locale`) and the rest of the message resolves.
344
+
345
+ ## Limits
346
+
347
+ Resolution is bounded three ways: 10 interpolation passes (a value referencing its own placeholder stops with its placeholders unresolved, reported as `pass-limit`), 100 000 UTF-16 code units of output (a pass that would exceed it is discarded and the last output under the bound stands, reported as `output-limit`) and 100 000 nodes per value conversion (a value past it is read as missing, reported as `unserializable-value`). The specification's conformance set, `@curly-message/conformance`, runs against this package's public API in its tests, at every level the format defines (Core, Intl, Extensions).
348
+
349
+ ## TypeScript
350
+
351
+ ```typescript
352
+ import { I18n } from '@sveltekit-i18n/base';
353
+ import parser from '@sveltekit-i18n/parser-curly';
354
+ import type { Config, Modifier, Report } from '@sveltekit-i18n/parser-curly';
355
+
356
+ type Payload = { applicationName: string };
357
+ type Props = { truncate?: { maxLength?: number } };
358
+
359
+ const truncate: Modifier.T<{ maxLength?: number }> = ({ value, props }) =>
360
+ value.length > (props.maxLength ?? 50) ? `${value.slice(0, props.maxLength ?? 50)}...` : value;
361
+
362
+ const config: Config<Payload, Props> = {
363
+ parser: parser({
364
+ customModifiers: { truncate },
365
+ onReport: (report: Report) => { console.warn(report.message); },
366
+ }),
367
+ loaders: [/* ... */],
368
+ };
369
+
370
+ const i18n = new I18n(config);
371
+
372
+ i18n.t('common.welcome', { applicationName: 'My app' }, { truncate: { maxLength: 20 } })
373
+ // → ok
374
+
375
+ i18n.t('common.welcome', { aplicationName: 'My app' })
376
+ // → type error: typo caught
377
+ ```
378
+
379
+ `Config<Payload, Props>` types the payload and the props `i18n.t` accepts; left bare, `Config` accepts any payload key and the built-in modifiers' props. A custom modifier types its own props through `Modifier.T<OwnProps>`. The factory's type arguments check the parser options the same way: with `Props` spelled as the second, `parser<Payload, Props>({ ... })`, a `modifierDefaults` entry for a custom modifier is checked; a modifier written inline reads typed `props` once the modifier names are spelled as the third argument too, `parser<Payload, Props, 'truncate'>({ ... })`. `Parser` holds the option and parameter types (`Parser.Options`, `Parser.OnReport`, `Parser.Params`, `Parser.Payload`), `Modifier` the modifier and wrapper types (`Modifier.T`, `Modifier.Wrapper`, `Modifier.Props`), and `Report` is the report.
380
+
381
+ ## Examples
382
+
383
+ See the [parser-curly example](https://github.com/sveltekit-i18n/lib/tree/master/examples/parser-default) for a complete working application.
384
+
385
+ **[Live Demo](https://parser-default.netlify.app)** – Interactive examples
386
+
387
+ ## Comparison with Other Parsers
388
+
389
+ ### vs. ICU Message Format
390
+
391
+ **parser-curly:**
392
+ ```json
393
+ {
394
+ "items": "You have {{count}} {{count; 1:item; default:items;}}."
395
+ }
396
+ ```
397
+
398
+ **ICU:**
399
+ ```json
400
+ {
401
+ "items": "You have {count} {count, plural, one {item} other {items}}."
402
+ }
403
+ ```
404
+
405
+ - `parser-curly` has simpler syntax
406
+ - ICU has more advanced plural rules for complex languages
407
+ - `parser-curly` has one dependency, the format's reference implementation, and no others
408
+ - ICU is an industry standard
409
+
410
+ Choose `parser-curly` for simplicity, ICU for standards compliance.
411
+
412
+ ## More Resources
413
+
414
+ - [All Parsers](https://github.com/sveltekit-i18n/parsers) – Parser overview
415
+ - [Curly Message Format](https://github.com/curly-message/spec) – The specification
416
+ - [Examples](https://github.com/sveltekit-i18n/lib/tree/master/examples) – Working examples
417
+ - [Changelog](./CHANGELOG.md) – Version history
418
+
419
+ ## Issues
420
+
421
+ If you're facing issues with this parser, create a ticket [here](https://github.com/sveltekit-i18n/lib/issues).
422
+
423
+ ## License
424
+
425
+ MIT
@@ -0,0 +1,35 @@
1
+ import { Parser as Parser$2, Config as Config$1 } from '@sveltekit-i18n/base';
2
+ import { Modifier, Parser as Parser$1 } from '@curly-message/parser';
3
+ export { Modifier, Report } from '@curly-message/parser';
4
+
5
+ declare namespace Parser {
6
+ /**
7
+ * The options `parser()` takes, handed on to `@curly-message/parser`:
8
+ * `customModifiers`, `modifierDefaults` and `onReport`. `onReport` is
9
+ * required, `null` included: this package writes to no channel of its own,
10
+ * so where a report goes is stated by whoever builds the parser.
11
+ */
12
+ type Options<Key extends string = Modifier.Key, Props = Modifier.DefaultProps> = Omit<Parser$1.Options<Key, Props>, 'onReport'> & {
13
+ onReport: OnReport | null | undefined;
14
+ };
15
+ type OnReport = Parser$1.OnReport;
16
+ type PayloadDefault = Parser$1.PayloadDefault;
17
+ type Payload<T = any, Props = Modifier.DefaultProps> = Parser$1.Payload<T, Props>;
18
+ /**
19
+ * The rest parameters of `t(key, payload?, props?)`: the values the message's
20
+ * placeholders name, then the per-call formatting options keyed by modifier
21
+ * name.
22
+ */
23
+ type Params<P = PayloadDefault, M = Modifier.DefaultProps> = [payload?: Payload<P, M>, props?: Modifier.Props<M>];
24
+ type T<P extends Parser$2.Params = Params> = Parser$2.T<P, string>;
25
+ type Factory = <Payload = {}, Props = {}, Key extends string = Modifier.Key>(options: Options<Key, Props>) => T<Params<Payload & PayloadDefault, Props & Modifier.DefaultProps>>;
26
+ }
27
+ /**
28
+ * The base config carrying this parser, typed by the payload the messages
29
+ * expect and by the formatting props a call may pass.
30
+ */
31
+ type Config<P = Parser.PayloadDefault, M = Modifier.DefaultProps> = Config$1.T<Parser.Params<P, M>, string>;
32
+
33
+ declare const parser: Parser.Factory;
34
+
35
+ export { type Config, Parser, parser as default };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import{createParser as i}from"@curly-message/parser";var f=r=>{let{resolve:e}=i(r);return{parse:(o,[t,p],s,a)=>e(o,{payload:t,props:p,locale:s,key:a})}},c=f;export{c as default};
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@sveltekit-i18n/parser-curly",
3
+ "version": "3.0.0-next.0",
4
+ "description": "Curly Message Format parser for the sveltekit-i18n library.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./package.json": "./package.json"
14
+ },
15
+ "engines": {
16
+ "node": ">=22"
17
+ },
18
+ "scripts": {
19
+ "dev": "tsup --watch",
20
+ "typecheck": "tsc --noEmit -p tsconfig.json",
21
+ "pretest": "npm run build && npm run typecheck",
22
+ "test": "vitest run",
23
+ "build": "tsup",
24
+ "prepublishOnly": "npm run build",
25
+ "lint": "eslint --fix .",
26
+ "prepare": "cd .. && simple-git-hooks parser-curly/.simple-git-hooks.json"
27
+ },
28
+ "files": [
29
+ "dist"
30
+ ],
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+ssh://git@github.com/sveltekit-i18n/parsers.git",
34
+ "directory": "parser-curly"
35
+ },
36
+ "keywords": [
37
+ "parser",
38
+ "curly",
39
+ "curly-message",
40
+ "i18n",
41
+ "sveltekit-i18n"
42
+ ],
43
+ "author": "Jarda Svoboda",
44
+ "license": "MIT",
45
+ "bugs": {
46
+ "url": "https://github.com/sveltekit-i18n/lib/issues"
47
+ },
48
+ "homepage": "https://github.com/sveltekit-i18n/parsers/tree/master/parser-curly#readme",
49
+ "peerDependencies": {
50
+ "@sveltekit-i18n/base": "^3.0.0-next.0"
51
+ },
52
+ "dependencies": {
53
+ "@curly-message/parser": "^1.0.0-next.2"
54
+ },
55
+ "devDependencies": {
56
+ "@curly-message/conformance": "^1.0.0-next.2",
57
+ "@eslint/js": "^10.0.1",
58
+ "@stylistic/eslint-plugin": "^5.10.0",
59
+ "@sveltekit-i18n/base": "^3.0.0-next.0",
60
+ "eslint": "^10.8.1",
61
+ "eslint-plugin-import-x": "^4.17.1",
62
+ "globals": "^17.11.0",
63
+ "simple-git-hooks": "^2.13.1",
64
+ "tsup": "^8.0.1",
65
+ "typescript": "^5.1.6",
66
+ "typescript-eslint": "^8.67.0",
67
+ "vitest": "^4.1.10"
68
+ }
69
+ }