chronolizer 0.2.0 → 0.4.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/README.md +170 -167
- package/dist/ast/constructors.d.mts +3 -8
- package/dist/ast/fold.d.mts +2 -1
- package/dist/ast/fold.mjs +38 -31
- package/dist/ast/normalize.mjs +4 -2
- package/dist/ast/schemas.d.mts +18 -25
- package/dist/ast/schemas.mjs +8 -17
- package/dist/filter/expression.mjs +27 -38
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/language/model.d.mts +33 -33
- package/dist/language/model.mjs +5 -8
- package/dist/language/registry.mjs +35 -33
- package/dist/locales/cs.mjs +253 -31
- package/dist/locales/de.mjs +366 -42
- package/dist/locales/en.mjs +215 -51
- package/dist/locales/es.mjs +498 -91
- package/dist/locales/fr.mjs +360 -65
- package/dist/locales/nl.mjs +302 -64
- package/dist/locales/pl.mjs +447 -135
- package/dist/locales/shared.mjs +192 -23
- package/dist/locales/tr.mjs +220 -41
- package/dist/natural/correction.d.mts +1 -1
- package/dist/natural/correction.mjs +31 -5
- package/dist/natural/format.mjs +1 -1
- package/dist/natural/parse.d.mts +1 -1
- package/dist/natural/parse.mjs +5 -4
- package/dist/natural/policy.mjs +2 -2
- package/dist/natural/suggest.mjs +19 -3
- package/dist/natural/suggestion.mjs +11 -5
- package/dist/resolve/resolve.mjs +2 -10
- package/package.json +1 -1
package/dist/locales/de.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { normalizeNaturalText } from "../natural/text.mjs";
|
|
|
4
4
|
import { defineLanguagePlugin, languagePluginsLayer } from "../language/registry.mjs";
|
|
5
5
|
import { correctWhitespaceSeparatedText } from "../natural/correction.mjs";
|
|
6
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";
|
|
7
|
+
import { absoluteDatePeriod, calendarPeriodOffset, candidate, compileCountAliasNormalizer, compoundCountAliases, countAliasVocabulary, currentYearDatePeriods, datedPeriods, datedQuarterPeriods, decimalTens, decomposeShiftedPeriodRange, fixedDatePeriod, fixedMonthPeriod, fixedQuarterPeriod, fixedYearPeriod, fromNowRange, futurePeriod, futureRange, isoDate, joinedNowCandidate, joinedPeriodCandidate, monthOfRelativeYear, namedCurrentYearDatePeriod, namedDatePeriod, openBoundaryCandidate, parseTrailingCount, periodBoundaryCandidate, periodEndDay, periodRange, periodStartDay, periodToDateRange, periodsFromPhrases, quarterOfRelativeYear, relativePeriod, relativeWeekday, relativeWeekend, remainingPeriodRange, renderPeriodRange, sequentialCountAliases, shiftPeriod, textAt, trailingPeriod, trailingRange, untilNowRange, validYear } from "./shared.mjs";
|
|
8
8
|
import { Effect, Option, String as String$1 } from "effect";
|
|
9
9
|
//#region src/locales/de.ts
|
|
10
10
|
const months = [
|
|
@@ -21,6 +21,42 @@ const months = [
|
|
|
21
21
|
"november",
|
|
22
22
|
"dezember"
|
|
23
23
|
];
|
|
24
|
+
const germanCountWords = [
|
|
25
|
+
"zwei",
|
|
26
|
+
"drei",
|
|
27
|
+
"vier",
|
|
28
|
+
"fünf",
|
|
29
|
+
"sechs",
|
|
30
|
+
"sieben",
|
|
31
|
+
"acht",
|
|
32
|
+
"neun",
|
|
33
|
+
"zehn",
|
|
34
|
+
"elf",
|
|
35
|
+
"zwölf",
|
|
36
|
+
"dreizehn",
|
|
37
|
+
"vierzehn",
|
|
38
|
+
"fünfzehn",
|
|
39
|
+
"sechzehn",
|
|
40
|
+
"siebzehn",
|
|
41
|
+
"achtzehn",
|
|
42
|
+
"neunzehn",
|
|
43
|
+
"zwanzig"
|
|
44
|
+
];
|
|
45
|
+
const germanCountOnes = [[1, "ein"], ...germanCountWords.slice(0, 8).map((word, index) => [index + 2, word])];
|
|
46
|
+
const germanCountAliases = [...sequentialCountAliases(germanCountWords.map((word) => [word]), 2), ...compoundCountAliases(decimalTens([
|
|
47
|
+
"zwanzig",
|
|
48
|
+
"dreißig",
|
|
49
|
+
"vierzig",
|
|
50
|
+
"fünfzig",
|
|
51
|
+
"sechzig",
|
|
52
|
+
"siebzig",
|
|
53
|
+
"achtzig",
|
|
54
|
+
"neunzig"
|
|
55
|
+
]), germanCountOnes, (ten, one) => [`${one}und${ten}`])];
|
|
56
|
+
const normalizeGermanCounts = compileCountAliasNormalizer(germanCountAliases);
|
|
57
|
+
const germanCountVocabulary = new Set(countAliasVocabulary(germanCountAliases));
|
|
58
|
+
const correctGerman = (input, vocabulary) => correctWhitespaceSeparatedText(input, vocabulary, germanCountVocabulary);
|
|
59
|
+
const normalizeGerman = (input, locale) => normalizeGermanCounts(normalizeNaturalText(input, locale));
|
|
24
60
|
const monthAbbreviations = [
|
|
25
61
|
["jan"],
|
|
26
62
|
["feb"],
|
|
@@ -45,6 +81,10 @@ const unitPhrases = [
|
|
|
45
81
|
next: "morgen",
|
|
46
82
|
compound: "tages",
|
|
47
83
|
currentGenitive: "dieses tages",
|
|
84
|
+
previousGenitive: "letzten tages",
|
|
85
|
+
nextGenitive: "nächsten tages",
|
|
86
|
+
indefiniteDative: "einem tag",
|
|
87
|
+
indefiniteGenitive: "eines tages",
|
|
48
88
|
dative: "tagen",
|
|
49
89
|
genitive: "des tages"
|
|
50
90
|
},
|
|
@@ -57,6 +97,10 @@ const unitPhrases = [
|
|
|
57
97
|
next: "nächste woche",
|
|
58
98
|
compound: "wochen",
|
|
59
99
|
currentGenitive: "dieser woche",
|
|
100
|
+
previousGenitive: "letzter woche",
|
|
101
|
+
nextGenitive: "nächster woche",
|
|
102
|
+
indefiniteDative: "einer woche",
|
|
103
|
+
indefiniteGenitive: "einer woche",
|
|
60
104
|
dative: "wochen",
|
|
61
105
|
genitive: "der woche"
|
|
62
106
|
},
|
|
@@ -69,6 +113,10 @@ const unitPhrases = [
|
|
|
69
113
|
next: "nächster monat",
|
|
70
114
|
compound: "monats",
|
|
71
115
|
currentGenitive: "dieses monats",
|
|
116
|
+
previousGenitive: "letzten monats",
|
|
117
|
+
nextGenitive: "nächsten monats",
|
|
118
|
+
indefiniteDative: "einem monat",
|
|
119
|
+
indefiniteGenitive: "eines monats",
|
|
72
120
|
dative: "monaten",
|
|
73
121
|
genitive: "des monats"
|
|
74
122
|
},
|
|
@@ -81,6 +129,10 @@ const unitPhrases = [
|
|
|
81
129
|
next: "nächstes quartal",
|
|
82
130
|
compound: "quartals",
|
|
83
131
|
currentGenitive: "dieses quartals",
|
|
132
|
+
previousGenitive: "letzten quartals",
|
|
133
|
+
nextGenitive: "nächsten quartals",
|
|
134
|
+
indefiniteDative: "einem quartal",
|
|
135
|
+
indefiniteGenitive: "eines quartals",
|
|
84
136
|
dative: "quartalen",
|
|
85
137
|
genitive: "des quartals"
|
|
86
138
|
},
|
|
@@ -93,11 +145,33 @@ const unitPhrases = [
|
|
|
93
145
|
next: "nächstes jahr",
|
|
94
146
|
compound: "jahres",
|
|
95
147
|
currentGenitive: "dieses jahres",
|
|
148
|
+
previousGenitive: "letzten jahres",
|
|
149
|
+
nextGenitive: "nächsten jahres",
|
|
150
|
+
indefiniteDative: "einem jahr",
|
|
151
|
+
indefiniteGenitive: "eines jahres",
|
|
96
152
|
dative: "jahren",
|
|
97
153
|
genitive: "des jahres"
|
|
98
154
|
}
|
|
99
155
|
];
|
|
100
156
|
const title = (value) => `${value.slice(0, 1).toLocaleUpperCase("de")}${value.slice(1)}`;
|
|
157
|
+
const weekdays = [
|
|
158
|
+
"montag",
|
|
159
|
+
"dienstag",
|
|
160
|
+
"mittwoch",
|
|
161
|
+
"donnerstag",
|
|
162
|
+
"freitag",
|
|
163
|
+
"samstag",
|
|
164
|
+
"sonntag"
|
|
165
|
+
];
|
|
166
|
+
const nextWeekdays = weekdays.flatMap((weekday, day) => [{
|
|
167
|
+
phrase: `nächster ${weekday}`,
|
|
168
|
+
day,
|
|
169
|
+
canonical: `nächster ${title(weekday)}`
|
|
170
|
+
}, {
|
|
171
|
+
phrase: `nächsten ${weekday}`,
|
|
172
|
+
day,
|
|
173
|
+
canonical: `nächster ${title(weekday)}`
|
|
174
|
+
}]);
|
|
101
175
|
const canonicalToDate = (entry) => `seit ${title(entry.compound)}beginn`;
|
|
102
176
|
const canonicalRelative = (value) => {
|
|
103
177
|
const separator = value.lastIndexOf(" ");
|
|
@@ -141,7 +215,125 @@ const currentBoundaryPhrases = unitPhrases.flatMap((entry) => [
|
|
|
141
215
|
entry,
|
|
142
216
|
phrase
|
|
143
217
|
})));
|
|
218
|
+
const declinedBoundaryPeriods = [
|
|
219
|
+
[
|
|
220
|
+
"diese woche",
|
|
221
|
+
"week",
|
|
222
|
+
0,
|
|
223
|
+
"dieser woche",
|
|
224
|
+
"diese woche"
|
|
225
|
+
],
|
|
226
|
+
[
|
|
227
|
+
"letzte woche",
|
|
228
|
+
"week",
|
|
229
|
+
-1,
|
|
230
|
+
"letzter woche",
|
|
231
|
+
"letzte woche"
|
|
232
|
+
],
|
|
233
|
+
[
|
|
234
|
+
"nächste woche",
|
|
235
|
+
"week",
|
|
236
|
+
1,
|
|
237
|
+
"nächster woche",
|
|
238
|
+
"nächste woche"
|
|
239
|
+
],
|
|
240
|
+
[
|
|
241
|
+
"dieser monat",
|
|
242
|
+
"month",
|
|
243
|
+
0,
|
|
244
|
+
"diesem monat",
|
|
245
|
+
"diesen monat"
|
|
246
|
+
],
|
|
247
|
+
[
|
|
248
|
+
"letzter monat",
|
|
249
|
+
"month",
|
|
250
|
+
-1,
|
|
251
|
+
"letztem monat",
|
|
252
|
+
"letzten monat"
|
|
253
|
+
],
|
|
254
|
+
[
|
|
255
|
+
"nächster monat",
|
|
256
|
+
"month",
|
|
257
|
+
1,
|
|
258
|
+
"nächstem monat",
|
|
259
|
+
"nächsten monat"
|
|
260
|
+
],
|
|
261
|
+
[
|
|
262
|
+
"dieses quartal",
|
|
263
|
+
"quarter",
|
|
264
|
+
0,
|
|
265
|
+
"diesem quartal",
|
|
266
|
+
"dieses quartal"
|
|
267
|
+
],
|
|
268
|
+
[
|
|
269
|
+
"letztes quartal",
|
|
270
|
+
"quarter",
|
|
271
|
+
-1,
|
|
272
|
+
"letztem quartal",
|
|
273
|
+
"letztes quartal"
|
|
274
|
+
],
|
|
275
|
+
[
|
|
276
|
+
"nächstes quartal",
|
|
277
|
+
"quarter",
|
|
278
|
+
1,
|
|
279
|
+
"nächstem quartal",
|
|
280
|
+
"nächstes quartal"
|
|
281
|
+
],
|
|
282
|
+
[
|
|
283
|
+
"dieses jahr",
|
|
284
|
+
"year",
|
|
285
|
+
0,
|
|
286
|
+
"diesem jahr",
|
|
287
|
+
"dieses jahr"
|
|
288
|
+
],
|
|
289
|
+
[
|
|
290
|
+
"letztes jahr",
|
|
291
|
+
"year",
|
|
292
|
+
-1,
|
|
293
|
+
"letztem jahr",
|
|
294
|
+
"letztes jahr"
|
|
295
|
+
],
|
|
296
|
+
[
|
|
297
|
+
"nächstes jahr",
|
|
298
|
+
"year",
|
|
299
|
+
1,
|
|
300
|
+
"nächstem jahr",
|
|
301
|
+
"nächstes jahr"
|
|
302
|
+
]
|
|
303
|
+
];
|
|
144
304
|
const relativeAliases = [
|
|
305
|
+
...unitPhrases.flatMap((entry) => [
|
|
306
|
+
[
|
|
307
|
+
entry.currentGenitive,
|
|
308
|
+
entry.unit,
|
|
309
|
+
0,
|
|
310
|
+
entry.current
|
|
311
|
+
],
|
|
312
|
+
[
|
|
313
|
+
entry.previousGenitive,
|
|
314
|
+
entry.unit,
|
|
315
|
+
-1,
|
|
316
|
+
entry.previous
|
|
317
|
+
],
|
|
318
|
+
[
|
|
319
|
+
entry.nextGenitive,
|
|
320
|
+
entry.unit,
|
|
321
|
+
1,
|
|
322
|
+
entry.next
|
|
323
|
+
]
|
|
324
|
+
]),
|
|
325
|
+
...declinedBoundaryPeriods.map(([canonical, unit, direction, dative]) => [
|
|
326
|
+
dative,
|
|
327
|
+
unit,
|
|
328
|
+
direction,
|
|
329
|
+
canonical
|
|
330
|
+
]),
|
|
331
|
+
[
|
|
332
|
+
"vorgestern",
|
|
333
|
+
"day",
|
|
334
|
+
-2,
|
|
335
|
+
"vorgestern"
|
|
336
|
+
],
|
|
145
337
|
[
|
|
146
338
|
"vorletzten tag",
|
|
147
339
|
"day",
|
|
@@ -414,6 +606,20 @@ const monthNumber = (value) => {
|
|
|
414
606
|
const shortIndex = monthAbbreviations.findIndex((aliases) => aliases.some((alias) => alias === normalized));
|
|
415
607
|
return shortIndex === -1 ? void 0 : shortIndex + 1;
|
|
416
608
|
};
|
|
609
|
+
const currentDateLabel = (day, month) => `${day}. ${title(textAt(months, month - 1))}`;
|
|
610
|
+
const withRelativeCase = (canonical, grammaticalCase) => {
|
|
611
|
+
const period = declinedBoundaryPeriods.find((entry) => canonical === canonicalRelative(entry[0]));
|
|
612
|
+
if (period === void 0) return canonical;
|
|
613
|
+
return canonicalRelative(period[grammaticalCase === "dative" ? 3 : 4]);
|
|
614
|
+
};
|
|
615
|
+
const withRelativeGenitive = (canonical) => {
|
|
616
|
+
for (const entry of unitPhrases) {
|
|
617
|
+
if (canonical === canonicalRelative(entry.current)) return canonicalRelative(entry.currentGenitive);
|
|
618
|
+
if (canonical === canonicalRelative(entry.previous)) return canonicalRelative(entry.previousGenitive);
|
|
619
|
+
if (canonical === canonicalRelative(entry.next)) return canonicalRelative(entry.nextGenitive);
|
|
620
|
+
}
|
|
621
|
+
return canonical;
|
|
622
|
+
};
|
|
417
623
|
const parseNamedDate = (input) => {
|
|
418
624
|
const named = String$1.match(/^([0-3]?\d)\.? ([a-zäöüß]+\.?) (\d{4})$/u)(input);
|
|
419
625
|
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}`);
|
|
@@ -427,7 +633,8 @@ const parseNamedDate = (input) => {
|
|
|
427
633
|
if (isIsoDate(value) && value !== "9999-12-31") return Option.some(fixedDatePeriod(value, `${day}. ${title(textAt(months, month - 1))} ${year}`));
|
|
428
634
|
}
|
|
429
635
|
}
|
|
430
|
-
|
|
636
|
+
const current = String$1.match(/^([0-3]?\d)\.? ([a-zäöüß]+\.?)$/u)(input);
|
|
637
|
+
return Option.isSome(current) ? namedCurrentYearDatePeriod(textAt(current.value, 2), textAt(current.value, 1), monthNumber, currentDateLabel) : Option.none();
|
|
431
638
|
};
|
|
432
639
|
const quarterNames = [
|
|
433
640
|
"erstes",
|
|
@@ -442,8 +649,8 @@ const quarterNumber = (value) => {
|
|
|
442
649
|
return index === -1 ? void 0 : index + 1;
|
|
443
650
|
};
|
|
444
651
|
const relativeDirection = (value) => {
|
|
445
|
-
if (value
|
|
446
|
-
if (value
|
|
652
|
+
if (value.startsWith("letzt")) return -1;
|
|
653
|
+
if (value.startsWith("nächst")) return 1;
|
|
447
654
|
return 0;
|
|
448
655
|
};
|
|
449
656
|
const parseQuarter = (input) => {
|
|
@@ -471,12 +678,13 @@ const parseQuarter = (input) => {
|
|
|
471
678
|
const quarter = quarterNumber(textAt(standalone.value, 1));
|
|
472
679
|
return quarter === void 0 ? Option.none() : Option.some(quarterOfRelativeYear(quarter, 0, `Q${quarter}`));
|
|
473
680
|
};
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
681
|
+
const parseDatedPeriod = (input) => {
|
|
682
|
+
const knownPeriod = Option.firstSomeOf([
|
|
683
|
+
absoluteDatePeriod(input, "de"),
|
|
684
|
+
parseNamedDate(input),
|
|
685
|
+
parseQuarter(input)
|
|
686
|
+
]);
|
|
687
|
+
if (Option.isSome(knownPeriod)) return knownPeriod;
|
|
480
688
|
const yearMatch = String$1.match(/^(?:(?:das )?(?:kalender)?jahr )?(\d{4})$/u)(input);
|
|
481
689
|
if (Option.isSome(yearMatch)) {
|
|
482
690
|
const year = validYear(textAt(yearMatch.value, 1));
|
|
@@ -488,6 +696,18 @@ const parseBasePeriod = (input) => {
|
|
|
488
696
|
const year = validYear(textAt(monthYear.value, 2));
|
|
489
697
|
if (month !== void 0 && year !== void 0) return Option.some(fixedMonthPeriod(year, month, `${title(textAt(months, month - 1))} ${year}`));
|
|
490
698
|
}
|
|
699
|
+
return Option.none();
|
|
700
|
+
};
|
|
701
|
+
const parseRelativeMonth = (input) => {
|
|
702
|
+
const prefixedRelativeMonth = String$1.match(/^(letzter|dieser|nächster) ([a-zäöüß]+\.?)$/u)(input);
|
|
703
|
+
if (Option.isSome(prefixedRelativeMonth)) {
|
|
704
|
+
const month = monthNumber(textAt(prefixedRelativeMonth.value, 2));
|
|
705
|
+
if (month !== void 0) {
|
|
706
|
+
const modifier = textAt(prefixedRelativeMonth.value, 1);
|
|
707
|
+
const direction = relativeDirection(modifier);
|
|
708
|
+
return Option.some(monthOfRelativeYear(month, direction, `${modifier} ${title(textAt(months, month - 1))}`));
|
|
709
|
+
}
|
|
710
|
+
}
|
|
491
711
|
const relativeMonth = String$1.match(/^([a-zäöüß]+\.?) (letzten|dieses|nächsten) jahres$/u)(input);
|
|
492
712
|
if (Option.isSome(relativeMonth)) {
|
|
493
713
|
const directionText = textAt(relativeMonth.value, 2);
|
|
@@ -497,11 +717,18 @@ const parseBasePeriod = (input) => {
|
|
|
497
717
|
}
|
|
498
718
|
const standaloneMonth = monthNumber(input);
|
|
499
719
|
if (standaloneMonth !== void 0) return Option.some(monthOfRelativeYear(standaloneMonth, 0, title(textAt(months, standaloneMonth - 1))));
|
|
720
|
+
return Option.none();
|
|
721
|
+
};
|
|
722
|
+
const parseBasePeriod = (input) => {
|
|
723
|
+
const knownPeriod = Option.firstSomeOf([parseDatedPeriod(input), parseRelativeMonth(input)]);
|
|
724
|
+
if (Option.isSome(knownPeriod)) return knownPeriod;
|
|
500
725
|
if (input === "wochenende" || input === "dieses wochenende" || input === "am wochenende") return Option.some(relativeWeekend(0, "dieses Wochenende"));
|
|
501
726
|
if (input === "letztes wochenende" || input === "vergangenes wochenende") return Option.some(relativeWeekend(-1, "letztes Wochenende"));
|
|
502
727
|
if (input === "nächstes wochenende" || input === "kommendes wochenende") return Option.some(relativeWeekend(1, "nächstes Wochenende"));
|
|
503
728
|
if (input === "vorletztes wochenende") return Option.some(relativeWeekend(-2, "vorletztes Wochenende"));
|
|
504
729
|
if (input === "übernächstes wochenende") return Option.some(relativeWeekend(2, "übernächstes Wochenende"));
|
|
730
|
+
const weekday = nextWeekdays.find((entry) => entry.phrase === input);
|
|
731
|
+
if (weekday !== void 0) return Option.some(relativeWeekday(weekday.day, 1, weekday.canonical));
|
|
505
732
|
const relative = unitPhrases.find((entry) => entry.current === input || entry.previous === input || entry.next === input);
|
|
506
733
|
if (relative !== void 0) {
|
|
507
734
|
let direction = 0;
|
|
@@ -514,29 +741,52 @@ const parseBasePeriod = (input) => {
|
|
|
514
741
|
return Option.none();
|
|
515
742
|
};
|
|
516
743
|
const parsePeriod = (input) => {
|
|
744
|
+
const shifted = String$1.match(/^(.+) (vor|in) ([1-9]\d*) (tag|tagen|woche|wochen|monat|monaten|quartal|quartalen|jahr|jahren)$/u)(input);
|
|
745
|
+
if (Option.isSome(shifted)) {
|
|
746
|
+
const amount = parseTrailingCount(textAt(shifted.value, 3));
|
|
747
|
+
const unitText = textAt(shifted.value, 4);
|
|
748
|
+
const entry = unitPhrases.find((unit) => [
|
|
749
|
+
unit.noun,
|
|
750
|
+
unit.plural,
|
|
751
|
+
unit.dative
|
|
752
|
+
].includes(unitText));
|
|
753
|
+
const period = parsePeriod(textAt(shifted.value, 1));
|
|
754
|
+
if (Option.isSome(amount) && entry !== void 0 && Option.isSome(period)) {
|
|
755
|
+
const past = textAt(shifted.value, 2) === "vor";
|
|
756
|
+
const direction = past ? -amount.value : amount.value;
|
|
757
|
+
const noun = amount.value === 1 ? entry.noun : entry.dative;
|
|
758
|
+
const canonical = `${period.value.canonical} ${past ? "vor" : "in"} ${amount.value} ${title(noun)}`;
|
|
759
|
+
return Option.some(shiftPeriod(period.value, direction, entry.unit, canonical));
|
|
760
|
+
}
|
|
761
|
+
}
|
|
517
762
|
const edge = String$1.match(/^(anfang|beginn|ende) (.+)$/u)(input);
|
|
518
763
|
if (Option.isSome(edge)) {
|
|
519
764
|
const edgeName = textAt(edge.value, 1);
|
|
520
|
-
const
|
|
765
|
+
const periodText = textAt(edge.value, 2);
|
|
766
|
+
const basePeriod = parseBasePeriod(periodText);
|
|
767
|
+
const implicit = unitPhrases.find((entry) => entry.genitive === periodText);
|
|
768
|
+
const period = Option.isSome(basePeriod) || implicit === void 0 ? basePeriod : Option.some(relativePeriod(implicit.unit, 0, implicit.current));
|
|
521
769
|
if (Option.isSome(period)) {
|
|
522
|
-
const canonical = `${edgeName === "ende" ? "Ende" : "Anfang"} ${period.value.canonical}`;
|
|
770
|
+
const canonical = `${edgeName === "ende" ? "Ende" : "Anfang"} ${withRelativeGenitive(period.value.canonical)}`;
|
|
523
771
|
return Option.some(edgeName === "ende" ? periodEndDay(period.value, canonical) : periodStartDay(period.value, canonical));
|
|
524
772
|
}
|
|
525
773
|
}
|
|
526
774
|
const wrapper = [
|
|
527
775
|
"im ",
|
|
776
|
+
"am ",
|
|
528
777
|
"für ",
|
|
529
778
|
"während "
|
|
530
779
|
].find((prefix) => input.startsWith(prefix));
|
|
531
|
-
|
|
780
|
+
const base = parseBasePeriod(wrapper === void 0 ? input : input.slice(wrapper.length));
|
|
781
|
+
return Option.isSome(base) ? base : parseCalendarOffset(input);
|
|
532
782
|
};
|
|
533
783
|
const currentBoundaryCandidate = (input) => {
|
|
534
784
|
const match = currentBoundaryPhrases.find((entry) => entry.phrase === input);
|
|
535
785
|
if (match === void 0) return Option.none();
|
|
536
786
|
const period = relativePeriod(match.entry.unit, 0, match.entry.current);
|
|
537
|
-
return Option.some(periodBoundaryCandidate(period, match.boundary, match.phrase));
|
|
787
|
+
return Option.some(periodBoundaryCandidate(period, match.boundary, canonicalRelative(match.phrase)));
|
|
538
788
|
};
|
|
539
|
-
const boundaryCandidate = (input) => openBoundaryCandidate(input, [
|
|
789
|
+
const boundaryCandidate = (input) => Option.map(openBoundaryCandidate(input, [
|
|
540
790
|
["seit dem beginn von ", "since"],
|
|
541
791
|
["seit dem anfang von ", "since"],
|
|
542
792
|
["seit dem jahr ", "since"],
|
|
@@ -563,12 +813,22 @@ const boundaryCandidate = (input) => openBoundaryCandidate(input, [
|
|
|
563
813
|
["nach ", "after"],
|
|
564
814
|
["bis ", "before"],
|
|
565
815
|
["ab ", "since"]
|
|
566
|
-
], parsePeriod);
|
|
816
|
+
], parsePeriod), (result) => candidate(result.range, canonicalRelative(input)));
|
|
567
817
|
const countedUnit = (value, amount, dative = false) => unitPhrases.find((entry) => {
|
|
568
818
|
if (amount === 1) return value === entry.noun;
|
|
569
819
|
return value === (dative ? entry.dative : entry.plural);
|
|
570
820
|
});
|
|
571
821
|
const parseCalendarOffset = (input) => {
|
|
822
|
+
const singular = String$1.match(/^(vor|in) (einem tag|einer woche|einem monat|einem quartal|einem jahr)$/u)(input);
|
|
823
|
+
if (Option.isSome(singular)) {
|
|
824
|
+
const entry = unitPhrases.find((unit) => unit.indefiniteDative === textAt(singular.value, 2));
|
|
825
|
+
if (entry !== void 0) {
|
|
826
|
+
const isPast = textAt(singular.value, 1) === "vor";
|
|
827
|
+
const direction = isPast ? -1 : 1;
|
|
828
|
+
const canonical = `${isPast ? "vor" : "in"} ${canonicalRelative(entry.indefiniteDative)}`;
|
|
829
|
+
return Option.some(relativePeriod(entry.unit, direction, canonical));
|
|
830
|
+
}
|
|
831
|
+
}
|
|
572
832
|
const prefixed = String$1.match(/^(vor|in) ([1-9]\d*) (tag|tagen|woche|wochen|monat|monaten|quartal|quartalen|jahr|jahren)$/u)(input);
|
|
573
833
|
const prior = String$1.match(/^([1-9]\d*) (tag|tage|woche|wochen|monat|monate|quartal|quartale|jahr|jahre) zuvor$/u)(input);
|
|
574
834
|
const match = Option.firstSomeOf([prefixed, prior]);
|
|
@@ -584,53 +844,79 @@ const parseCalendarOffset = (input) => {
|
|
|
584
844
|
const directionName = isPast ? "vor" : "in";
|
|
585
845
|
const noun = amount.value === 1 ? entry.noun : entry.dative;
|
|
586
846
|
const canonical = `${directionName} ${amount.value} ${title(noun)}`;
|
|
587
|
-
return Option.some(
|
|
847
|
+
return Option.some(relativePeriod(entry.unit, direction, canonical));
|
|
588
848
|
};
|
|
589
849
|
const parseRollingPeriod = (input) => {
|
|
590
850
|
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);
|
|
851
|
+
const pastDative = String$1.match(/^(?:(?:in den letzten|in den vergangenen|in den vorherigen) |seit )([1-9]\d*) (tag|tagen|woche|wochen|monat|monaten|quartal|quartalen|jahr|jahren)$/u)(input);
|
|
852
|
+
const singularSince = String$1.match(/^seit (einem tag|einer woche|einem monat|einem quartal|einem jahr)$/u)(input);
|
|
592
853
|
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
854
|
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
855
|
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);
|
|
856
|
+
const singularFuture = String$1.match(/^innerhalb (eines tages|einer woche|eines monats|eines quartals|eines jahres)$/u)(input);
|
|
595
857
|
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
858
|
const match = Option.firstSomeOf([
|
|
597
859
|
past,
|
|
598
860
|
pastDative,
|
|
861
|
+
singularSince,
|
|
599
862
|
pastGenitive,
|
|
600
863
|
future,
|
|
601
864
|
futureDative,
|
|
865
|
+
singularFuture,
|
|
602
866
|
futureGenitive
|
|
603
867
|
]);
|
|
604
868
|
if (Option.isNone(match)) return Option.none();
|
|
605
|
-
const
|
|
869
|
+
const isSingular = Option.isSome(singularSince) || Option.isSome(singularFuture);
|
|
870
|
+
const amount = parseTrailingCount(isSingular ? "1" : textAt(match.value, 1));
|
|
606
871
|
if (Option.isNone(amount)) return Option.none();
|
|
607
|
-
const usesDative = Option.isSome(pastDative) || Option.isSome(futureDative);
|
|
608
|
-
const
|
|
872
|
+
const usesDative = Option.isSome(pastDative) || Option.isSome(singularSince) || Option.isSome(futureDative);
|
|
873
|
+
const sinceEntry = Option.isSome(singularSince) ? unitPhrases.find((unit) => unit.indefiniteDative === textAt(match.value, 1)) : void 0;
|
|
874
|
+
const futureEntry = Option.isSome(singularFuture) ? unitPhrases.find((unit) => unit.indefiniteGenitive === textAt(match.value, 1)) : void 0;
|
|
875
|
+
const countedEntry = isSingular ? void 0 : countedUnit(textAt(match.value, 2), amount.value, usesDative);
|
|
876
|
+
const entry = sinceEntry ?? futureEntry ?? countedEntry;
|
|
609
877
|
if (entry === void 0) return Option.none();
|
|
610
|
-
const isFuture = Option.isSome(future) || Option.isSome(futureDative) || Option.isSome(futureGenitive);
|
|
878
|
+
const isFuture = Option.isSome(future) || Option.isSome(futureDative) || Option.isSome(singularFuture) || Option.isSome(futureGenitive);
|
|
611
879
|
const range = isFuture ? futureRange(amount.value, entry.unit) : trailingRange(amount.value, entry.unit);
|
|
880
|
+
if (amount.value === 1) {
|
|
881
|
+
const phrase = isFuture ? `innerhalb ${canonicalRelative(entry.indefiniteGenitive)}` : `seit ${canonicalRelative(entry.indefiniteDative)}`;
|
|
882
|
+
return Option.some(candidate(range, phrase));
|
|
883
|
+
}
|
|
612
884
|
const direction = isFuture ? "nächste" : "letzte";
|
|
613
|
-
|
|
614
|
-
|
|
885
|
+
return Option.some(candidate(range, `${direction} ${amount.value} ${title(entry.plural)}`));
|
|
886
|
+
};
|
|
887
|
+
const parseElidedDateRange = (input) => {
|
|
888
|
+
const match = String$1.match(/^(?:vom )?([0-3]?\d)\.(?: bis |[–—-])(?:zum )?([0-3]?\d)\.? ([a-zäöüß]+\.?)(?: (\d{4}))?$/u)(input);
|
|
889
|
+
if (Option.isNone(match)) return Option.none();
|
|
890
|
+
const lowerDay = textAt(match.value, 1);
|
|
891
|
+
const upperDay = textAt(match.value, 2);
|
|
892
|
+
const month = textAt(match.value, 3);
|
|
893
|
+
const year = textAt(match.value, 4);
|
|
894
|
+
const suffix = year.length === 0 ? "" : ` ${year}`;
|
|
895
|
+
return joinedPeriodCandidate(`von ${lowerDay}. ${month}${suffix} bis ${upperDay}. ${month}${suffix}`, [["von ", " bis "]], parsePeriod, (lower, upper) => `von ${lower} bis ${upper}`);
|
|
615
896
|
};
|
|
616
897
|
const parseGerman = (input) => {
|
|
617
898
|
const remaining = remainingPeriodPhrases.find((entry) => entry.phrase === input);
|
|
618
899
|
if (remaining !== void 0) return Option.some(candidate(remainingPeriodRange(remaining.entry.unit), `Rest ${canonicalRelative(remaining.entry.genitive)}`));
|
|
619
900
|
if (["bis heute", "bis jetzt"].includes(input)) return Option.some(candidate(untilNowRange(), "bis heute"));
|
|
620
901
|
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
902
|
const rolling = parseRollingPeriod(input);
|
|
624
903
|
if (Option.isSome(rolling)) return rolling;
|
|
625
904
|
const toDate = toDatePhrases.find((entry) => entry.phrase === input);
|
|
626
905
|
if (toDate !== void 0) return Option.some(candidate(periodToDateRange(toDate.entry.unit), canonicalToDate(toDate.entry)));
|
|
627
906
|
const currentBoundary = currentBoundaryCandidate(input);
|
|
628
907
|
if (Option.isSome(currentBoundary)) return currentBoundary;
|
|
908
|
+
const elided = parseElidedDateRange(input);
|
|
909
|
+
if (Option.isSome(elided)) return elided;
|
|
629
910
|
const nowBounded = joinedNowCandidate(input, [
|
|
630
911
|
["von ", " bis heute"],
|
|
631
912
|
["vom ", " bis heute"],
|
|
632
913
|
["zwischen ", " und heute"]
|
|
633
|
-
], [
|
|
914
|
+
], [
|
|
915
|
+
"von heute bis ",
|
|
916
|
+
"zwischen heute und ",
|
|
917
|
+
"heute bis ",
|
|
918
|
+
"jetzt bis "
|
|
919
|
+
], parsePeriod, (period) => `von ${period} bis heute`, (period) => `von heute bis ${period}`);
|
|
634
920
|
if (Option.isSome(nowBounded)) return nowBounded;
|
|
635
921
|
const bounded = joinedPeriodCandidate(input, [
|
|
636
922
|
["von ", " bis einschließlich "],
|
|
@@ -657,9 +943,10 @@ const parseGerman = (input) => {
|
|
|
657
943
|
if (Option.isSome(bounded)) return bounded;
|
|
658
944
|
const boundary = boundaryCandidate(input);
|
|
659
945
|
if (Option.isSome(boundary)) return boundary;
|
|
660
|
-
return Option.map(
|
|
946
|
+
return parsePeriod(input).pipe(Option.map((period) => candidate(periodRange(period), period.canonical)));
|
|
661
947
|
};
|
|
662
948
|
const staticPeriodPhrases = [
|
|
949
|
+
"vorgestern",
|
|
663
950
|
"dieses wochenende",
|
|
664
951
|
"letztes wochenende",
|
|
665
952
|
"nächstes wochenende",
|
|
@@ -669,12 +956,12 @@ const staticPeriodPhrases = [
|
|
|
669
956
|
entry.current,
|
|
670
957
|
entry.previous,
|
|
671
958
|
entry.next,
|
|
672
|
-
`anfang ${entry.
|
|
673
|
-
`ende ${entry.
|
|
674
|
-
`anfang ${entry.
|
|
675
|
-
`ende ${entry.
|
|
676
|
-
`anfang ${entry.
|
|
677
|
-
`ende ${entry.
|
|
959
|
+
`anfang ${entry.currentGenitive}`,
|
|
960
|
+
`ende ${entry.currentGenitive}`,
|
|
961
|
+
`anfang ${entry.previousGenitive}`,
|
|
962
|
+
`ende ${entry.previousGenitive}`,
|
|
963
|
+
`anfang ${entry.nextGenitive}`,
|
|
964
|
+
`ende ${entry.nextGenitive}`
|
|
678
965
|
]),
|
|
679
966
|
...[
|
|
680
967
|
1,
|
|
@@ -688,9 +975,12 @@ const staticPeriodPhrases = [
|
|
|
688
975
|
]),
|
|
689
976
|
...months.flatMap((month) => [
|
|
690
977
|
month,
|
|
978
|
+
`letzter ${month}`,
|
|
979
|
+
`nächster ${month}`,
|
|
691
980
|
`${month} letzten jahres`,
|
|
692
981
|
`${month} nächsten jahres`
|
|
693
|
-
])
|
|
982
|
+
]),
|
|
983
|
+
...nextWeekdays.map((entry) => entry.phrase)
|
|
694
984
|
];
|
|
695
985
|
const staticPeriods = periodsFromPhrases(staticPeriodPhrases, parsePeriod);
|
|
696
986
|
const boundaryPrefixes = [
|
|
@@ -719,8 +1009,21 @@ const countedSuggestions = (input) => {
|
|
|
719
1009
|
const germanSuggestionPhrases = [
|
|
720
1010
|
...unitPhrases.map(canonicalToDate),
|
|
721
1011
|
...unitPhrases.map((entry) => `rest ${entry.genitive}`),
|
|
1012
|
+
...unitPhrases.flatMap((entry) => [
|
|
1013
|
+
`seit ${entry.indefiniteDative}`,
|
|
1014
|
+
`innerhalb ${entry.indefiniteGenitive}`,
|
|
1015
|
+
`vor ${entry.indefiniteDative}`,
|
|
1016
|
+
`in ${entry.indefiniteDative}`
|
|
1017
|
+
]),
|
|
722
1018
|
...staticPeriodPhrases,
|
|
723
|
-
...
|
|
1019
|
+
...currentBoundaryPhrases.map((entry) => entry.phrase),
|
|
1020
|
+
...declinedBoundaryPeriods.flatMap(([, , , dative, accusative]) => [
|
|
1021
|
+
`seit ${dative}`,
|
|
1022
|
+
`vor ${dative}`,
|
|
1023
|
+
`bis einschließlich ${accusative}`,
|
|
1024
|
+
`nach ${dative}`
|
|
1025
|
+
]),
|
|
1026
|
+
...prefixNaturalPhrases(months, boundaryPrefixes),
|
|
724
1027
|
"bis heute",
|
|
725
1028
|
"ab jetzt"
|
|
726
1029
|
].map((phrase) => normalizeNaturalText(phrase, "de"));
|
|
@@ -734,6 +1037,17 @@ const suggestGerman = (input, limit) => {
|
|
|
734
1037
|
], limit);
|
|
735
1038
|
};
|
|
736
1039
|
const renderGerman = (range) => {
|
|
1040
|
+
const shifted = decomposeShiftedPeriodRange(range);
|
|
1041
|
+
if (Option.isSome(shifted)) {
|
|
1042
|
+
const base = renderGerman(shifted.value.baseRange);
|
|
1043
|
+
const entry = unitPhrases.find((unit) => unit.unit === shifted.value.unit);
|
|
1044
|
+
if (Option.isSome(base) && entry !== void 0) {
|
|
1045
|
+
const amount = Math.abs(shifted.value.amount);
|
|
1046
|
+
const noun = amount === 1 ? entry.noun : entry.dative;
|
|
1047
|
+
const direction = shifted.value.amount < 0 ? "vor" : "in";
|
|
1048
|
+
return Option.some(`${base.value} ${direction} ${amount} ${title(noun)}`);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
737
1051
|
const offset = calendarPeriodOffset(range);
|
|
738
1052
|
if (Option.isSome(offset) && Math.abs(offset.value.amount) > 1) {
|
|
739
1053
|
const entry = unitPhrases.find((unit) => unit.unit === offset.value.unit);
|
|
@@ -742,20 +1056,26 @@ const renderGerman = (range) => {
|
|
|
742
1056
|
const future = futurePeriod(range);
|
|
743
1057
|
if (Option.isSome(future)) {
|
|
744
1058
|
const entry = unitPhrases.find((unit) => unit.unit === future.value.unit);
|
|
745
|
-
if (entry !== void 0) return Option.some(
|
|
1059
|
+
if (entry !== void 0) return Option.some(future.value.amount === 1 ? `innerhalb ${canonicalRelative(entry.indefiniteGenitive)}` : `nächste ${future.value.amount} ${title(entry.plural)}`);
|
|
746
1060
|
}
|
|
747
1061
|
const trailing = trailingPeriod(range);
|
|
748
1062
|
if (Option.isSome(trailing)) {
|
|
749
1063
|
const entry = unitPhrases.find((unit) => unit.unit === trailing.value.unit);
|
|
750
|
-
if (entry !== void 0) return Option.some(trailing.value.amount === 1 ? `
|
|
1064
|
+
if (entry !== void 0) return Option.some(trailing.value.amount === 1 ? `seit ${canonicalRelative(entry.indefiniteDative)}` : `letzte ${trailing.value.amount} ${title(entry.plural)}`);
|
|
751
1065
|
}
|
|
752
|
-
const
|
|
753
|
-
|
|
1066
|
+
const dated = [...datedPeriods(range, months), ...datedQuarterPeriods(range)];
|
|
1067
|
+
const periods = [
|
|
1068
|
+
...staticPeriods,
|
|
1069
|
+
...currentYearDatePeriods(range, currentDateLabel),
|
|
1070
|
+
...periodsFromPhrases([...dated, ...dated.flatMap((phrase) => [`anfang ${phrase}`, `ende ${phrase}`])], parsePeriod)
|
|
1071
|
+
];
|
|
1072
|
+
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 ${withRelativeCase(period, "dative")}`, (period) => `vor ${withRelativeCase(period, "dative")}`, (period) => `bis einschließlich ${withRelativeCase(period, "accusative")}`, (period) => `nach ${withRelativeCase(period, "dative")}`, (lower, upper) => `von ${lower} bis ${upper}`, (period) => `von ${period} bis heute`, (period) => `von heute bis ${period}`, () => "bis heute", () => "ab jetzt");
|
|
754
1073
|
};
|
|
755
1074
|
const GermanContribution = new BaseLanguageContribution({
|
|
756
1075
|
locale: "de",
|
|
757
1076
|
vocabulary: [
|
|
758
1077
|
...months,
|
|
1078
|
+
...weekdays,
|
|
759
1079
|
...quarterNames,
|
|
760
1080
|
"q1",
|
|
761
1081
|
"q2",
|
|
@@ -770,7 +1090,11 @@ const GermanContribution = new BaseLanguageContribution({
|
|
|
770
1090
|
entry.noun,
|
|
771
1091
|
entry.plural,
|
|
772
1092
|
entry.dative,
|
|
1093
|
+
...entry.indefiniteDative.split(" "),
|
|
1094
|
+
...entry.indefiniteGenitive.split(" "),
|
|
773
1095
|
...entry.currentGenitive.split(" "),
|
|
1096
|
+
...entry.previousGenitive.split(" "),
|
|
1097
|
+
...entry.nextGenitive.split(" "),
|
|
774
1098
|
...entry.current.split(" "),
|
|
775
1099
|
...entry.previous.split(" "),
|
|
776
1100
|
...entry.next.split(" ")
|
|
@@ -813,8 +1137,8 @@ const GermanContribution = new BaseLanguageContribution({
|
|
|
813
1137
|
"zwischen",
|
|
814
1138
|
"zuvor"
|
|
815
1139
|
],
|
|
816
|
-
normalize:
|
|
817
|
-
correct:
|
|
1140
|
+
normalize: normalizeGerman,
|
|
1141
|
+
correct: correctGerman,
|
|
818
1142
|
parseExact: parseGerman,
|
|
819
1143
|
suggest: suggestGerman,
|
|
820
1144
|
render: renderGerman
|