chronolizer 0.1.0 → 0.2.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.
Files changed (62) hide show
  1. package/README.md +70 -7
  2. package/dist/ast/constructors.d.mts +140 -0
  3. package/dist/ast/constructors.mjs +25 -0
  4. package/dist/ast/fold.d.mts +12 -0
  5. package/dist/ast/fold.mjs +51 -0
  6. package/dist/ast/normalize.d.mts +42 -0
  7. package/dist/ast/normalize.mjs +32 -0
  8. package/dist/ast/schemas.d.mts +87 -0
  9. package/dist/ast/schemas.mjs +74 -0
  10. package/dist/filter/codec.d.mts +92 -0
  11. package/dist/filter/codec.mjs +48 -0
  12. package/dist/filter/errors.d.mts +14 -0
  13. package/dist/filter/errors.mjs +10 -0
  14. package/dist/filter/expression.d.mts +8 -0
  15. package/dist/filter/expression.mjs +93 -0
  16. package/dist/filter/schema.d.mts +13 -0
  17. package/dist/filter/schema.mjs +11 -0
  18. package/dist/filter/transformation.d.mts +37 -0
  19. package/dist/filter/transformation.mjs +20 -0
  20. package/dist/index.d.mts +22 -969
  21. package/dist/index.mjs +22 -2305
  22. package/dist/language/errors.d.mts +38 -0
  23. package/dist/language/errors.mjs +30 -0
  24. package/dist/language/model.d.mts +239 -0
  25. package/dist/language/model.mjs +53 -0
  26. package/dist/language/registry.d.mts +17 -0
  27. package/dist/language/registry.mjs +134 -0
  28. package/dist/locales/cs.d.mts +10 -0
  29. package/dist/locales/cs.mjs +693 -0
  30. package/dist/locales/de.d.mts +10 -0
  31. package/dist/locales/de.mjs +828 -0
  32. package/dist/locales/en.d.mts +10 -0
  33. package/dist/locales/en.mjs +550 -0
  34. package/dist/locales/es.d.mts +10 -0
  35. package/dist/locales/es.mjs +660 -0
  36. package/dist/locales/fr.d.mts +10 -0
  37. package/dist/locales/fr.mjs +737 -0
  38. package/dist/locales/nl.d.mts +10 -0
  39. package/dist/locales/nl.mjs +652 -0
  40. package/dist/locales/pl.d.mts +10 -0
  41. package/dist/locales/pl.mjs +711 -0
  42. package/dist/locales/shared.mjs +289 -0
  43. package/dist/locales/tr.d.mts +10 -0
  44. package/dist/locales/tr.mjs +660 -0
  45. package/dist/natural/correction.d.mts +13 -0
  46. package/dist/natural/correction.mjs +73 -0
  47. package/dist/natural/format.d.mts +47 -0
  48. package/dist/natural/format.mjs +14 -0
  49. package/dist/natural/parse.d.mts +99 -0
  50. package/dist/natural/parse.mjs +66 -0
  51. package/dist/natural/policy.mjs +6 -0
  52. package/dist/natural/suggest.d.mts +53 -0
  53. package/dist/natural/suggest.mjs +24 -0
  54. package/dist/natural/suggestion.d.mts +4 -0
  55. package/dist/natural/suggestion.mjs +79 -0
  56. package/dist/natural/text.d.mts +5 -0
  57. package/dist/natural/text.mjs +5 -0
  58. package/dist/resolve/resolve.d.mts +79 -0
  59. package/dist/resolve/resolve.mjs +66 -0
  60. package/dist/resolve/schema.d.mts +59 -0
  61. package/dist/resolve/schema.mjs +28 -0
  62. package/package.json +15 -3
@@ -0,0 +1,828 @@
1
+ import { isIsoDate } from "../ast/schemas.mjs";
2
+ import { BaseLanguageContribution } from "../language/model.mjs";
3
+ import { normalizeNaturalText } from "../natural/text.mjs";
4
+ import { defineLanguagePlugin, languagePluginsLayer } from "../language/registry.mjs";
5
+ import { correctWhitespaceSeparatedText } from "../natural/correction.mjs";
6
+ import { completeNaturalPhrases, fixedCalendarPeriodPhrases, naturalCount, prefixNaturalPhrases } from "../natural/suggestion.mjs";
7
+ import { calendarPeriodOffset, candidate, datedPeriods, datedQuarterPeriods, fixedDatePeriod, fixedMonthPeriod, fixedQuarterPeriod, fixedYearPeriod, fromNowRange, futurePeriod, futureRange, isoDate, joinedNowCandidate, joinedPeriodCandidate, monthOfRelativeYear, namedDatePeriod, openBoundaryCandidate, parseTrailingCount, periodBoundaryCandidate, periodEndDay, periodRange, periodStartDay, periodToDateRange, periodsFromPhrases, quarterOfRelativeYear, relativePeriod, relativeWeekend, remainingPeriodRange, renderPeriodRange, textAt, trailingPeriod, trailingRange, untilNowRange, validYear } from "./shared.mjs";
8
+ import { Effect, Option, String as String$1 } from "effect";
9
+ //#region src/locales/de.ts
10
+ const months = [
11
+ "januar",
12
+ "februar",
13
+ "märz",
14
+ "april",
15
+ "mai",
16
+ "juni",
17
+ "juli",
18
+ "august",
19
+ "september",
20
+ "oktober",
21
+ "november",
22
+ "dezember"
23
+ ];
24
+ const monthAbbreviations = [
25
+ ["jan"],
26
+ ["feb"],
27
+ ["mär", "mrz"],
28
+ ["apr"],
29
+ ["mai"],
30
+ ["jun"],
31
+ ["jul"],
32
+ ["aug"],
33
+ ["sep", "sept"],
34
+ ["okt"],
35
+ ["nov"],
36
+ ["dez"]
37
+ ];
38
+ const unitPhrases = [
39
+ {
40
+ unit: "day",
41
+ noun: "tag",
42
+ plural: "tage",
43
+ current: "heute",
44
+ previous: "gestern",
45
+ next: "morgen",
46
+ compound: "tages",
47
+ currentGenitive: "dieses tages",
48
+ dative: "tagen",
49
+ genitive: "des tages"
50
+ },
51
+ {
52
+ unit: "week",
53
+ noun: "woche",
54
+ plural: "wochen",
55
+ current: "diese woche",
56
+ previous: "letzte woche",
57
+ next: "nächste woche",
58
+ compound: "wochen",
59
+ currentGenitive: "dieser woche",
60
+ dative: "wochen",
61
+ genitive: "der woche"
62
+ },
63
+ {
64
+ unit: "month",
65
+ noun: "monat",
66
+ plural: "monate",
67
+ current: "dieser monat",
68
+ previous: "letzter monat",
69
+ next: "nächster monat",
70
+ compound: "monats",
71
+ currentGenitive: "dieses monats",
72
+ dative: "monaten",
73
+ genitive: "des monats"
74
+ },
75
+ {
76
+ unit: "quarter",
77
+ noun: "quartal",
78
+ plural: "quartale",
79
+ current: "dieses quartal",
80
+ previous: "letztes quartal",
81
+ next: "nächstes quartal",
82
+ compound: "quartals",
83
+ currentGenitive: "dieses quartals",
84
+ dative: "quartalen",
85
+ genitive: "des quartals"
86
+ },
87
+ {
88
+ unit: "year",
89
+ noun: "jahr",
90
+ plural: "jahre",
91
+ current: "dieses jahr",
92
+ previous: "letztes jahr",
93
+ next: "nächstes jahr",
94
+ compound: "jahres",
95
+ currentGenitive: "dieses jahres",
96
+ dative: "jahren",
97
+ genitive: "des jahres"
98
+ }
99
+ ];
100
+ const title = (value) => `${value.slice(0, 1).toLocaleUpperCase("de")}${value.slice(1)}`;
101
+ const canonicalToDate = (entry) => `seit ${title(entry.compound)}beginn`;
102
+ const canonicalRelative = (value) => {
103
+ const separator = value.lastIndexOf(" ");
104
+ if (separator === -1) return value;
105
+ return `${value.slice(0, separator + 1)}${title(value.slice(separator + 1))}`;
106
+ };
107
+ const remainingPeriodPhrases = unitPhrases.flatMap((entry) => [`rest ${entry.genitive}`, `rest ${entry.currentGenitive}`].map((phrase) => ({
108
+ entry,
109
+ phrase
110
+ })));
111
+ const toDatePhrases = unitPhrases.flatMap((entry) => [
112
+ `seit ${entry.compound}beginn`,
113
+ `seit ${entry.compound}anfang`,
114
+ `seit beginn ${entry.genitive}`,
115
+ `seit anfang ${entry.genitive}`,
116
+ `seit beginn ${entry.currentGenitive}`,
117
+ `seit anfang ${entry.currentGenitive}`,
118
+ `vom ${entry.compound}beginn bis heute`,
119
+ `vom ${entry.compound}anfang bis heute`,
120
+ `vom beginn ${entry.genitive} bis heute`,
121
+ `vom anfang ${entry.genitive} bis heute`,
122
+ `vom beginn ${entry.currentGenitive} bis heute`,
123
+ `vom anfang ${entry.currentGenitive} bis heute`,
124
+ `${entry.noun} bis heute`,
125
+ `${entry.current} bisher`,
126
+ `bisher ${entry.current}`
127
+ ].map((phrase) => ({
128
+ entry,
129
+ phrase
130
+ })));
131
+ const currentBoundaryPhrases = unitPhrases.flatMap((entry) => [
132
+ [`vor ${entry.compound}beginn`, "before"],
133
+ [`vor ${entry.compound}anfang`, "before"],
134
+ [`bis ${entry.compound}beginn`, "before"],
135
+ [`bis ${entry.compound}anfang`, "before"],
136
+ [`bis ${entry.compound}ende`, "through"],
137
+ [`nach ${entry.compound}ende`, "after"],
138
+ [`ab ${entry.compound}ende`, "after"]
139
+ ].map(([phrase, boundary]) => ({
140
+ boundary,
141
+ entry,
142
+ phrase
143
+ })));
144
+ const relativeAliases = [
145
+ [
146
+ "vorletzten tag",
147
+ "day",
148
+ -2,
149
+ "vorletzter tag"
150
+ ],
151
+ [
152
+ "übernächsten tag",
153
+ "day",
154
+ 2,
155
+ "übernächster tag"
156
+ ],
157
+ [
158
+ "vorletzte woche",
159
+ "week",
160
+ -2,
161
+ "vorletzte woche"
162
+ ],
163
+ [
164
+ "übernächste woche",
165
+ "week",
166
+ 2,
167
+ "übernächste woche"
168
+ ],
169
+ [
170
+ "vorletzten monat",
171
+ "month",
172
+ -2,
173
+ "vorletzter monat"
174
+ ],
175
+ [
176
+ "übernächsten monat",
177
+ "month",
178
+ 2,
179
+ "übernächster monat"
180
+ ],
181
+ [
182
+ "vorletztes quartal",
183
+ "quarter",
184
+ -2,
185
+ "vorletztes quartal"
186
+ ],
187
+ [
188
+ "übernächstes quartal",
189
+ "quarter",
190
+ 2,
191
+ "übernächstes quartal"
192
+ ],
193
+ [
194
+ "vorletztes jahr",
195
+ "year",
196
+ -2,
197
+ "vorletztes jahr"
198
+ ],
199
+ [
200
+ "übernächstes jahr",
201
+ "year",
202
+ 2,
203
+ "übernächstes jahr"
204
+ ],
205
+ [
206
+ "letzten tag",
207
+ "day",
208
+ -1,
209
+ "gestern"
210
+ ],
211
+ [
212
+ "vergangenen tag",
213
+ "day",
214
+ -1,
215
+ "gestern"
216
+ ],
217
+ [
218
+ "vorherigen tag",
219
+ "day",
220
+ -1,
221
+ "gestern"
222
+ ],
223
+ [
224
+ "diesen tag",
225
+ "day",
226
+ 0,
227
+ "heute"
228
+ ],
229
+ [
230
+ "heutigen tag",
231
+ "day",
232
+ 0,
233
+ "heute"
234
+ ],
235
+ [
236
+ "nächsten tag",
237
+ "day",
238
+ 1,
239
+ "morgen"
240
+ ],
241
+ [
242
+ "kommenden tag",
243
+ "day",
244
+ 1,
245
+ "morgen"
246
+ ],
247
+ [
248
+ "vergangene woche",
249
+ "week",
250
+ -1,
251
+ "letzte woche"
252
+ ],
253
+ [
254
+ "vorherige woche",
255
+ "week",
256
+ -1,
257
+ "letzte woche"
258
+ ],
259
+ [
260
+ "aktuelle woche",
261
+ "week",
262
+ 0,
263
+ "diese woche"
264
+ ],
265
+ [
266
+ "laufende woche",
267
+ "week",
268
+ 0,
269
+ "diese woche"
270
+ ],
271
+ [
272
+ "kommende woche",
273
+ "week",
274
+ 1,
275
+ "nächste woche"
276
+ ],
277
+ [
278
+ "folgende woche",
279
+ "week",
280
+ 1,
281
+ "nächste woche"
282
+ ],
283
+ [
284
+ "letzten monat",
285
+ "month",
286
+ -1,
287
+ "letzter monat"
288
+ ],
289
+ [
290
+ "vergangenen monat",
291
+ "month",
292
+ -1,
293
+ "letzter monat"
294
+ ],
295
+ [
296
+ "vorherigen monat",
297
+ "month",
298
+ -1,
299
+ "letzter monat"
300
+ ],
301
+ [
302
+ "diesen monat",
303
+ "month",
304
+ 0,
305
+ "dieser monat"
306
+ ],
307
+ [
308
+ "aktuellen monat",
309
+ "month",
310
+ 0,
311
+ "dieser monat"
312
+ ],
313
+ [
314
+ "laufenden monat",
315
+ "month",
316
+ 0,
317
+ "dieser monat"
318
+ ],
319
+ [
320
+ "nächsten monat",
321
+ "month",
322
+ 1,
323
+ "nächster monat"
324
+ ],
325
+ [
326
+ "kommenden monat",
327
+ "month",
328
+ 1,
329
+ "nächster monat"
330
+ ],
331
+ [
332
+ "folgenden monat",
333
+ "month",
334
+ 1,
335
+ "nächster monat"
336
+ ],
337
+ [
338
+ "vergangenes quartal",
339
+ "quarter",
340
+ -1,
341
+ "letztes quartal"
342
+ ],
343
+ [
344
+ "vorheriges quartal",
345
+ "quarter",
346
+ -1,
347
+ "letztes quartal"
348
+ ],
349
+ [
350
+ "aktuelles quartal",
351
+ "quarter",
352
+ 0,
353
+ "dieses quartal"
354
+ ],
355
+ [
356
+ "laufendes quartal",
357
+ "quarter",
358
+ 0,
359
+ "dieses quartal"
360
+ ],
361
+ [
362
+ "kommendes quartal",
363
+ "quarter",
364
+ 1,
365
+ "nächstes quartal"
366
+ ],
367
+ [
368
+ "folgendes quartal",
369
+ "quarter",
370
+ 1,
371
+ "nächstes quartal"
372
+ ],
373
+ [
374
+ "vergangenes jahr",
375
+ "year",
376
+ -1,
377
+ "letztes jahr"
378
+ ],
379
+ [
380
+ "vorheriges jahr",
381
+ "year",
382
+ -1,
383
+ "letztes jahr"
384
+ ],
385
+ [
386
+ "aktuelles jahr",
387
+ "year",
388
+ 0,
389
+ "dieses jahr"
390
+ ],
391
+ [
392
+ "laufendes jahr",
393
+ "year",
394
+ 0,
395
+ "dieses jahr"
396
+ ],
397
+ [
398
+ "kommendes jahr",
399
+ "year",
400
+ 1,
401
+ "nächstes jahr"
402
+ ],
403
+ [
404
+ "folgendes jahr",
405
+ "year",
406
+ 1,
407
+ "nächstes jahr"
408
+ ]
409
+ ];
410
+ const monthNumber = (value) => {
411
+ const normalized = value.endsWith(".") ? value.slice(0, -1) : value;
412
+ const fullIndex = months.findIndex((month) => month === normalized);
413
+ if (fullIndex !== -1) return fullIndex + 1;
414
+ const shortIndex = monthAbbreviations.findIndex((aliases) => aliases.some((alias) => alias === normalized));
415
+ return shortIndex === -1 ? void 0 : shortIndex + 1;
416
+ };
417
+ const parseNamedDate = (input) => {
418
+ const named = String$1.match(/^([0-3]?\d)\.? ([a-zäöüß]+\.?) (\d{4})$/u)(input);
419
+ if (Option.isSome(named)) return namedDatePeriod(textAt(named.value, 3), textAt(named.value, 2), textAt(named.value, 1), monthNumber, (day, month, year) => `${day}. ${title(textAt(months, month - 1))} ${year}`);
420
+ const numeric = String$1.match(/^([0-3]?\d)\.([01]?\d)\.(\d{4})$/u)(input);
421
+ if (Option.isSome(numeric)) {
422
+ const year = validYear(textAt(numeric.value, 3));
423
+ const day = Number(textAt(numeric.value, 1));
424
+ const month = Number(textAt(numeric.value, 2));
425
+ if (year !== void 0 && month >= 1 && month <= 12) {
426
+ const value = isoDate(year, month, day);
427
+ if (isIsoDate(value) && value !== "9999-12-31") return Option.some(fixedDatePeriod(value, `${day}. ${title(textAt(months, month - 1))} ${year}`));
428
+ }
429
+ }
430
+ return Option.none();
431
+ };
432
+ const quarterNames = [
433
+ "erstes",
434
+ "zweites",
435
+ "drittes",
436
+ "viertes"
437
+ ];
438
+ const quarterNumber = (value) => {
439
+ if (value.startsWith("q")) return Number(value.slice(1));
440
+ if (value.endsWith(".")) return Number(value.slice(0, -1));
441
+ const index = quarterNames.findIndex((name) => name === value);
442
+ return index === -1 ? void 0 : index + 1;
443
+ };
444
+ const relativeDirection = (value) => {
445
+ if (value === "letzten") return -1;
446
+ if (value === "nächsten") return 1;
447
+ return 0;
448
+ };
449
+ const parseQuarter = (input) => {
450
+ const fixed = String$1.match(/^(q[1-4]|[1-4]\.|erstes|zweites|drittes|viertes)(?: quartal)? (\d{4})$/u)(input);
451
+ const reversed = String$1.match(/^(\d{4}) (q[1-4])$/u)(input);
452
+ const relative = String$1.match(/^(q[1-4]|[1-4]\.|erstes|zweites|drittes|viertes)(?: quartal)? (letzten|dieses|nächsten) jahres$/u)(input);
453
+ const standalone = String$1.match(/^(q[1-4]|[1-4]\.|erstes|zweites|drittes|viertes)(?: quartal)?$/u)(input);
454
+ const fixedMatch = Option.firstSomeOf([fixed, reversed]);
455
+ if (Option.isSome(fixedMatch)) {
456
+ const quarterText = textAt(fixedMatch.value, Option.isSome(reversed) ? 2 : 1);
457
+ const yearText = textAt(fixedMatch.value, Option.isSome(reversed) ? 1 : 2);
458
+ const quarter = quarterNumber(quarterText);
459
+ const year = validYear(yearText);
460
+ if (quarter !== void 0 && year !== void 0) return Option.some(fixedQuarterPeriod(year, quarter, `Q${quarter} ${year}`));
461
+ }
462
+ if (Option.isSome(relative)) {
463
+ const directionText = textAt(relative.value, 2);
464
+ const quarter = quarterNumber(textAt(relative.value, 1));
465
+ if (quarter !== void 0) {
466
+ const direction = relativeDirection(directionText);
467
+ return Option.some(quarterOfRelativeYear(quarter, direction, `Q${quarter} ${directionText} Jahres`));
468
+ }
469
+ }
470
+ if (Option.isNone(standalone)) return Option.none();
471
+ const quarter = quarterNumber(textAt(standalone.value, 1));
472
+ return quarter === void 0 ? Option.none() : Option.some(quarterOfRelativeYear(quarter, 0, `Q${quarter}`));
473
+ };
474
+ const parseBasePeriod = (input) => {
475
+ if (isIsoDate(input) && input !== "9999-12-31") return Option.some(fixedDatePeriod(input, input));
476
+ const namedDate = parseNamedDate(input);
477
+ if (Option.isSome(namedDate)) return namedDate;
478
+ const quarter = parseQuarter(input);
479
+ if (Option.isSome(quarter)) return quarter;
480
+ const yearMatch = String$1.match(/^(?:(?:das )?(?:kalender)?jahr )?(\d{4})$/u)(input);
481
+ if (Option.isSome(yearMatch)) {
482
+ const year = validYear(textAt(yearMatch.value, 1));
483
+ if (year !== void 0) return Option.some(fixedYearPeriod(year, String(year)));
484
+ }
485
+ const monthYear = String$1.match(/^([a-zäöüß]+\.?) (\d{4})$/u)(input);
486
+ if (Option.isSome(monthYear)) {
487
+ const month = monthNumber(textAt(monthYear.value, 1));
488
+ const year = validYear(textAt(monthYear.value, 2));
489
+ if (month !== void 0 && year !== void 0) return Option.some(fixedMonthPeriod(year, month, `${title(textAt(months, month - 1))} ${year}`));
490
+ }
491
+ const relativeMonth = String$1.match(/^([a-zäöüß]+\.?) (letzten|dieses|nächsten) jahres$/u)(input);
492
+ if (Option.isSome(relativeMonth)) {
493
+ const directionText = textAt(relativeMonth.value, 2);
494
+ const month = monthNumber(textAt(relativeMonth.value, 1));
495
+ const direction = relativeDirection(directionText);
496
+ if (month !== void 0) return Option.some(monthOfRelativeYear(month, direction, `${title(textAt(months, month - 1))} ${directionText} Jahres`));
497
+ }
498
+ const standaloneMonth = monthNumber(input);
499
+ if (standaloneMonth !== void 0) return Option.some(monthOfRelativeYear(standaloneMonth, 0, title(textAt(months, standaloneMonth - 1))));
500
+ if (input === "wochenende" || input === "dieses wochenende" || input === "am wochenende") return Option.some(relativeWeekend(0, "dieses Wochenende"));
501
+ if (input === "letztes wochenende" || input === "vergangenes wochenende") return Option.some(relativeWeekend(-1, "letztes Wochenende"));
502
+ if (input === "nächstes wochenende" || input === "kommendes wochenende") return Option.some(relativeWeekend(1, "nächstes Wochenende"));
503
+ if (input === "vorletztes wochenende") return Option.some(relativeWeekend(-2, "vorletztes Wochenende"));
504
+ if (input === "übernächstes wochenende") return Option.some(relativeWeekend(2, "übernächstes Wochenende"));
505
+ const relative = unitPhrases.find((entry) => entry.current === input || entry.previous === input || entry.next === input);
506
+ if (relative !== void 0) {
507
+ let direction = 0;
508
+ if (input === relative.previous) direction = -1;
509
+ if (input === relative.next) direction = 1;
510
+ return Option.some(relativePeriod(relative.unit, direction, canonicalRelative(input)));
511
+ }
512
+ const alias = relativeAliases.find((entry) => entry[0] === input);
513
+ if (alias !== void 0) return Option.some(relativePeriod(alias[1], alias[2], canonicalRelative(alias[3])));
514
+ return Option.none();
515
+ };
516
+ const parsePeriod = (input) => {
517
+ const edge = String$1.match(/^(anfang|beginn|ende) (.+)$/u)(input);
518
+ if (Option.isSome(edge)) {
519
+ const edgeName = textAt(edge.value, 1);
520
+ const period = parseBasePeriod(textAt(edge.value, 2));
521
+ if (Option.isSome(period)) {
522
+ const canonical = `${edgeName === "ende" ? "Ende" : "Anfang"} ${period.value.canonical}`;
523
+ return Option.some(edgeName === "ende" ? periodEndDay(period.value, canonical) : periodStartDay(period.value, canonical));
524
+ }
525
+ }
526
+ const wrapper = [
527
+ "im ",
528
+ "für ",
529
+ "während "
530
+ ].find((prefix) => input.startsWith(prefix));
531
+ return parseBasePeriod(wrapper === void 0 ? input : input.slice(wrapper.length));
532
+ };
533
+ const currentBoundaryCandidate = (input) => {
534
+ const match = currentBoundaryPhrases.find((entry) => entry.phrase === input);
535
+ if (match === void 0) return Option.none();
536
+ const period = relativePeriod(match.entry.unit, 0, match.entry.current);
537
+ return Option.some(periodBoundaryCandidate(period, match.boundary, match.phrase));
538
+ };
539
+ const boundaryCandidate = (input) => openBoundaryCandidate(input, [
540
+ ["seit dem beginn von ", "since"],
541
+ ["seit dem anfang von ", "since"],
542
+ ["seit dem jahr ", "since"],
543
+ ["ab dem jahr ", "since"],
544
+ ["bis zum beginn von ", "before"],
545
+ ["bis zum anfang von ", "before"],
546
+ ["bis zum jahr ", "before"],
547
+ ["bis zum ende von ", "through"],
548
+ ["nach dem ende von ", "after"],
549
+ ["bis einschließlich ", "through"],
550
+ ["seit beginn ", "since"],
551
+ ["seit anfang ", "since"],
552
+ ["ab beginn ", "since"],
553
+ ["ab anfang ", "since"],
554
+ ["vor beginn ", "before"],
555
+ ["vor anfang ", "before"],
556
+ ["bis beginn ", "before"],
557
+ ["bis anfang ", "before"],
558
+ ["bis ende ", "through"],
559
+ ["nach ende ", "after"],
560
+ ["ab ende ", "after"],
561
+ ["seit ", "since"],
562
+ ["vor ", "before"],
563
+ ["nach ", "after"],
564
+ ["bis ", "before"],
565
+ ["ab ", "since"]
566
+ ], parsePeriod);
567
+ const countedUnit = (value, amount, dative = false) => unitPhrases.find((entry) => {
568
+ if (amount === 1) return value === entry.noun;
569
+ return value === (dative ? entry.dative : entry.plural);
570
+ });
571
+ const parseCalendarOffset = (input) => {
572
+ const prefixed = String$1.match(/^(vor|in) ([1-9]\d*) (tag|tagen|woche|wochen|monat|monaten|quartal|quartalen|jahr|jahren)$/u)(input);
573
+ const prior = String$1.match(/^([1-9]\d*) (tag|tage|woche|wochen|monat|monate|quartal|quartale|jahr|jahre) zuvor$/u)(input);
574
+ const match = Option.firstSomeOf([prefixed, prior]);
575
+ if (Option.isNone(match)) return Option.none();
576
+ const amountText = textAt(match.value, Option.isSome(prefixed) ? 2 : 1);
577
+ const unitText = textAt(match.value, Option.isSome(prefixed) ? 3 : 2);
578
+ const amount = parseTrailingCount(amountText);
579
+ if (Option.isNone(amount)) return Option.none();
580
+ const entry = countedUnit(unitText, amount.value, Option.isSome(prefixed));
581
+ if (entry === void 0) return Option.none();
582
+ const isPast = Option.isSome(prior) || Option.isSome(prefixed) && textAt(match.value, 1) === "vor";
583
+ const direction = isPast ? -amount.value : amount.value;
584
+ const directionName = isPast ? "vor" : "in";
585
+ const noun = amount.value === 1 ? entry.noun : entry.dative;
586
+ const canonical = `${directionName} ${amount.value} ${title(noun)}`;
587
+ return Option.some(candidate(periodRange(relativePeriod(entry.unit, direction, canonical)), canonical));
588
+ };
589
+ const parseRollingPeriod = (input) => {
590
+ const past = String$1.match(/^(?:(?:die letzten|letzten|letzte|vergangene|vorherige) )?([1-9]\d*) (tag|tage|woche|wochen|monat|monate|quartal|quartale|jahr|jahre)$/u)(input);
591
+ const pastDative = String$1.match(/^(?:in den letzten|in den vergangenen|in den vorherigen) ([1-9]\d*) (tag|tagen|woche|wochen|monat|monaten|quartal|quartalen|jahr|jahren)$/u)(input);
592
+ const pastGenitive = String$1.match(/^während der letzten ([1-9]\d*) (tag|tage|woche|wochen|monat|monate|quartal|quartale|jahr|jahre)$/u)(input);
593
+ const future = String$1.match(/^(?:die nächsten|nächsten|nächste|kommende) ([1-9]\d*) (tag|tage|woche|wochen|monat|monate|quartal|quartale|jahr|jahre)$/u)(input);
594
+ const futureDative = String$1.match(/^(?:in den nächsten|in den kommenden|innerhalb von) ([1-9]\d*) (tag|tagen|woche|wochen|monat|monaten|quartal|quartalen|jahr|jahren)$/u)(input);
595
+ const futureGenitive = String$1.match(/^innerhalb der nächsten ([1-9]\d*) (tag|tage|woche|wochen|monat|monate|quartal|quartale|jahr|jahre)$/u)(input);
596
+ const match = Option.firstSomeOf([
597
+ past,
598
+ pastDative,
599
+ pastGenitive,
600
+ future,
601
+ futureDative,
602
+ futureGenitive
603
+ ]);
604
+ if (Option.isNone(match)) return Option.none();
605
+ const amount = parseTrailingCount(textAt(match.value, 1));
606
+ if (Option.isNone(amount)) return Option.none();
607
+ const usesDative = Option.isSome(pastDative) || Option.isSome(futureDative);
608
+ const entry = countedUnit(textAt(match.value, 2), amount.value, usesDative);
609
+ if (entry === void 0) return Option.none();
610
+ const isFuture = Option.isSome(future) || Option.isSome(futureDative) || Option.isSome(futureGenitive);
611
+ const range = isFuture ? futureRange(amount.value, entry.unit) : trailingRange(amount.value, entry.unit);
612
+ const direction = isFuture ? "nächste" : "letzte";
613
+ const noun = amount.value === 1 ? entry.noun : entry.plural;
614
+ return Option.some(candidate(range, `${direction} ${amount.value} ${title(noun)}`));
615
+ };
616
+ const parseGerman = (input) => {
617
+ const remaining = remainingPeriodPhrases.find((entry) => entry.phrase === input);
618
+ if (remaining !== void 0) return Option.some(candidate(remainingPeriodRange(remaining.entry.unit), `Rest ${canonicalRelative(remaining.entry.genitive)}`));
619
+ if (["bis heute", "bis jetzt"].includes(input)) return Option.some(candidate(untilNowRange(), "bis heute"));
620
+ if (["ab jetzt", "von jetzt an"].includes(input)) return Option.some(candidate(fromNowRange(), "ab jetzt"));
621
+ const offset = parseCalendarOffset(input);
622
+ if (Option.isSome(offset)) return offset;
623
+ const rolling = parseRollingPeriod(input);
624
+ if (Option.isSome(rolling)) return rolling;
625
+ const toDate = toDatePhrases.find((entry) => entry.phrase === input);
626
+ if (toDate !== void 0) return Option.some(candidate(periodToDateRange(toDate.entry.unit), canonicalToDate(toDate.entry)));
627
+ const currentBoundary = currentBoundaryCandidate(input);
628
+ if (Option.isSome(currentBoundary)) return currentBoundary;
629
+ const nowBounded = joinedNowCandidate(input, [
630
+ ["von ", " bis heute"],
631
+ ["vom ", " bis heute"],
632
+ ["zwischen ", " und heute"]
633
+ ], ["von heute bis ", "zwischen heute und "], parsePeriod, (period) => `von ${period} bis heute`, (period) => `von heute bis ${period}`);
634
+ if (Option.isSome(nowBounded)) return nowBounded;
635
+ const bounded = joinedPeriodCandidate(input, [
636
+ ["von ", " bis einschließlich "],
637
+ ["vom ", " bis einschließlich "],
638
+ ["von ", " bis zum "],
639
+ ["vom ", " bis zum "],
640
+ ["von ", " bis "],
641
+ ["vom ", " bis "],
642
+ ["zwischen ", " und "],
643
+ ["", " bis einschließlich "],
644
+ ["", " bis "],
645
+ ["", " - "],
646
+ ["", " – "],
647
+ ["", " — "],
648
+ ["zwischen ", "-"],
649
+ ["", "-"],
650
+ ["zwischen ", "–"],
651
+ ["", "–"],
652
+ ["zwischen ", "—"],
653
+ ["", "—"],
654
+ ["zwischen ", "~"],
655
+ ["", "~"]
656
+ ], parsePeriod, (lower, upper) => `von ${lower} bis ${upper}`);
657
+ if (Option.isSome(bounded)) return bounded;
658
+ const boundary = boundaryCandidate(input);
659
+ if (Option.isSome(boundary)) return boundary;
660
+ return Option.map(parsePeriod(input), (period) => candidate(periodRange(period), period.canonical));
661
+ };
662
+ const staticPeriodPhrases = [
663
+ "dieses wochenende",
664
+ "letztes wochenende",
665
+ "nächstes wochenende",
666
+ "vorletztes wochenende",
667
+ "übernächstes wochenende",
668
+ ...unitPhrases.flatMap((entry) => [
669
+ entry.current,
670
+ entry.previous,
671
+ entry.next,
672
+ `anfang ${entry.current}`,
673
+ `ende ${entry.current}`,
674
+ `anfang ${entry.previous}`,
675
+ `ende ${entry.previous}`,
676
+ `anfang ${entry.next}`,
677
+ `ende ${entry.next}`
678
+ ]),
679
+ ...[
680
+ 1,
681
+ 2,
682
+ 3,
683
+ 4
684
+ ].flatMap((quarter) => [
685
+ `q${quarter}`,
686
+ `q${quarter} letzten jahres`,
687
+ `q${quarter} nächsten jahres`
688
+ ]),
689
+ ...months.flatMap((month) => [
690
+ month,
691
+ `${month} letzten jahres`,
692
+ `${month} nächsten jahres`
693
+ ])
694
+ ];
695
+ const staticPeriods = periodsFromPhrases(staticPeriodPhrases, parsePeriod);
696
+ const boundaryPrefixes = [
697
+ "seit ",
698
+ "vor ",
699
+ "bis einschließlich ",
700
+ "nach "
701
+ ];
702
+ const countedSuggestions = (input) => {
703
+ const amount = naturalCount(input);
704
+ if (amount === void 0) return [];
705
+ return unitPhrases.flatMap((entry) => {
706
+ const noun = amount === 1 ? entry.noun : entry.plural;
707
+ const dative = amount === 1 ? entry.noun : entry.dative;
708
+ return [
709
+ ...amount === 1 ? [`${amount} ${noun}`] : [
710
+ `letzte ${amount} ${noun}`,
711
+ `${amount} ${noun}`,
712
+ `nächste ${amount} ${noun}`
713
+ ],
714
+ `vor ${amount} ${dative}`,
715
+ `in ${amount} ${dative}`
716
+ ];
717
+ });
718
+ };
719
+ const germanSuggestionPhrases = [
720
+ ...unitPhrases.map(canonicalToDate),
721
+ ...unitPhrases.map((entry) => `rest ${entry.genitive}`),
722
+ ...staticPeriodPhrases,
723
+ ...prefixNaturalPhrases(staticPeriodPhrases, boundaryPrefixes),
724
+ "bis heute",
725
+ "ab jetzt"
726
+ ].map((phrase) => normalizeNaturalText(phrase, "de"));
727
+ const suggestGerman = (input, limit) => {
728
+ const fixed = fixedCalendarPeriodPhrases(input, months);
729
+ return completeNaturalPhrases(input, [
730
+ ...germanSuggestionPhrases,
731
+ ...fixed,
732
+ ...prefixNaturalPhrases(fixed, boundaryPrefixes),
733
+ ...countedSuggestions(input)
734
+ ], limit);
735
+ };
736
+ const renderGerman = (range) => {
737
+ const offset = calendarPeriodOffset(range);
738
+ if (Option.isSome(offset) && Math.abs(offset.value.amount) > 1) {
739
+ const entry = unitPhrases.find((unit) => unit.unit === offset.value.unit);
740
+ if (entry !== void 0) return Option.some(`${offset.value.amount < 0 ? "vor" : "in"} ${Math.abs(offset.value.amount)} ${title(Math.abs(offset.value.amount) === 1 ? entry.noun : entry.dative)}`);
741
+ }
742
+ const future = futurePeriod(range);
743
+ if (Option.isSome(future)) {
744
+ const entry = unitPhrases.find((unit) => unit.unit === future.value.unit);
745
+ if (entry !== void 0) return Option.some(`nächste ${future.value.amount} ${title(future.value.amount === 1 ? entry.noun : entry.plural)}`);
746
+ }
747
+ const trailing = trailingPeriod(range);
748
+ if (Option.isSome(trailing)) {
749
+ const entry = unitPhrases.find((unit) => unit.unit === trailing.value.unit);
750
+ if (entry !== void 0) return Option.some(trailing.value.amount === 1 ? `1 ${title(entry.noun)}` : `letzte ${trailing.value.amount} ${title(entry.plural)}`);
751
+ }
752
+ const periods = [...staticPeriods, ...periodsFromPhrases([...datedPeriods(range, months), ...datedQuarterPeriods(range)], parsePeriod)];
753
+ return renderPeriodRange(range, [...unitPhrases.map((entry) => candidate(periodToDateRange(entry.unit), canonicalToDate(entry))), ...unitPhrases.map((entry) => candidate(remainingPeriodRange(entry.unit), `Rest ${canonicalRelative(entry.genitive)}`))], periods, (period) => `seit ${period}`, (period) => `vor ${period}`, (period) => `bis einschließlich ${period}`, (period) => `nach ${period}`, (lower, upper) => `von ${lower} bis ${upper}`, (period) => `von ${period} bis heute`, (period) => `von heute bis ${period}`, () => "bis heute", () => "ab jetzt");
754
+ };
755
+ const GermanContribution = new BaseLanguageContribution({
756
+ locale: "de",
757
+ vocabulary: [
758
+ ...months,
759
+ ...quarterNames,
760
+ "q1",
761
+ "q2",
762
+ "q3",
763
+ "q4",
764
+ ...monthAbbreviations.flatMap((aliases) => aliases),
765
+ ...toDatePhrases.flatMap((entry) => entry.phrase.split(" ")),
766
+ ...remainingPeriodPhrases.flatMap((entry) => entry.phrase.split(" ")),
767
+ ...currentBoundaryPhrases.flatMap((entry) => entry.phrase.split(" ")),
768
+ ...relativeAliases.flatMap((entry) => entry[0].split(" ")),
769
+ ...unitPhrases.flatMap((entry) => [
770
+ entry.noun,
771
+ entry.plural,
772
+ entry.dative,
773
+ ...entry.currentGenitive.split(" "),
774
+ ...entry.current.split(" "),
775
+ ...entry.previous.split(" "),
776
+ ...entry.next.split(" ")
777
+ ]),
778
+ "ab",
779
+ "an",
780
+ "anfang",
781
+ "beginn",
782
+ "bisher",
783
+ "bis",
784
+ "dem",
785
+ "den",
786
+ "die",
787
+ "dieses",
788
+ "einschließlich",
789
+ "ende",
790
+ "für",
791
+ "im",
792
+ "innerhalb",
793
+ "jetzt",
794
+ "kalenderjahr",
795
+ "jahres",
796
+ "kommende",
797
+ "kommenden",
798
+ "letzte",
799
+ "letzten",
800
+ "nach",
801
+ "nächste",
802
+ "nächsten",
803
+ "seit",
804
+ "vor",
805
+ "vorherige",
806
+ "vorherigen",
807
+ "vergangene",
808
+ "vergangenen",
809
+ "vom",
810
+ "von",
811
+ "während",
812
+ "wochenende",
813
+ "zwischen",
814
+ "zuvor"
815
+ ],
816
+ normalize: normalizeNaturalText,
817
+ correct: correctWhitespaceSeparatedText,
818
+ parseExact: parseGerman,
819
+ suggest: suggestGerman,
820
+ render: renderGerman
821
+ });
822
+ const GermanLanguage = defineLanguagePlugin({
823
+ id: "chronolizer/language-de",
824
+ effect: (context) => Effect.asVoid(context.register("chronolizer/language-de", GermanContribution))
825
+ });
826
+ const GermanLanguageLayer = languagePluginsLayer([GermanLanguage]);
827
+ //#endregion
828
+ export { GermanContribution, GermanLanguage, GermanLanguageLayer };