chronolizer 0.2.0 → 0.3.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 +169 -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 +3 -20
- 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 +4 -7
- package/dist/language/registry.mjs +3 -21
- package/dist/locales/cs.mjs +70 -17
- package/dist/locales/de.mjs +237 -26
- package/dist/locales/en.mjs +69 -21
- package/dist/locales/es.mjs +325 -77
- package/dist/locales/fr.mjs +215 -51
- package/dist/locales/nl.mjs +189 -50
- package/dist/locales/pl.mjs +295 -121
- package/dist/locales/shared.mjs +129 -23
- package/dist/locales/tr.mjs +86 -21
- package/dist/natural/parse.d.mts +1 -1
- package/dist/natural/policy.mjs +2 -2
- package/dist/resolve/resolve.mjs +1 -9
- 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, currentYearDatePeriods, datedPeriods, datedQuarterPeriods, fixedDatePeriod, fixedMonthPeriod, fixedQuarterPeriod, fixedYearPeriod, fromNowRange, futurePeriod, futureRange, isoDate, joinedNowCandidate, joinedPeriodCandidate, monthOfRelativeYear, namedCurrentYearDatePeriod, namedDatePeriod, openBoundaryCandidate, parseTrailingCount, periodBoundaryCandidate, periodEndDay, periodRange, periodStartDay, periodToDateRange, periodsFromPhrases, quarterOfRelativeYear, relativePeriod, relativeWeekend, remainingPeriodRange, renderPeriodRange, 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 = [
|
|
@@ -45,6 +45,10 @@ const unitPhrases = [
|
|
|
45
45
|
next: "morgen",
|
|
46
46
|
compound: "tages",
|
|
47
47
|
currentGenitive: "dieses tages",
|
|
48
|
+
previousGenitive: "letzten tages",
|
|
49
|
+
nextGenitive: "nächsten tages",
|
|
50
|
+
indefiniteDative: "einem tag",
|
|
51
|
+
indefiniteGenitive: "eines tages",
|
|
48
52
|
dative: "tagen",
|
|
49
53
|
genitive: "des tages"
|
|
50
54
|
},
|
|
@@ -57,6 +61,10 @@ const unitPhrases = [
|
|
|
57
61
|
next: "nächste woche",
|
|
58
62
|
compound: "wochen",
|
|
59
63
|
currentGenitive: "dieser woche",
|
|
64
|
+
previousGenitive: "letzter woche",
|
|
65
|
+
nextGenitive: "nächster woche",
|
|
66
|
+
indefiniteDative: "einer woche",
|
|
67
|
+
indefiniteGenitive: "einer woche",
|
|
60
68
|
dative: "wochen",
|
|
61
69
|
genitive: "der woche"
|
|
62
70
|
},
|
|
@@ -69,6 +77,10 @@ const unitPhrases = [
|
|
|
69
77
|
next: "nächster monat",
|
|
70
78
|
compound: "monats",
|
|
71
79
|
currentGenitive: "dieses monats",
|
|
80
|
+
previousGenitive: "letzten monats",
|
|
81
|
+
nextGenitive: "nächsten monats",
|
|
82
|
+
indefiniteDative: "einem monat",
|
|
83
|
+
indefiniteGenitive: "eines monats",
|
|
72
84
|
dative: "monaten",
|
|
73
85
|
genitive: "des monats"
|
|
74
86
|
},
|
|
@@ -81,6 +93,10 @@ const unitPhrases = [
|
|
|
81
93
|
next: "nächstes quartal",
|
|
82
94
|
compound: "quartals",
|
|
83
95
|
currentGenitive: "dieses quartals",
|
|
96
|
+
previousGenitive: "letzten quartals",
|
|
97
|
+
nextGenitive: "nächsten quartals",
|
|
98
|
+
indefiniteDative: "einem quartal",
|
|
99
|
+
indefiniteGenitive: "eines quartals",
|
|
84
100
|
dative: "quartalen",
|
|
85
101
|
genitive: "des quartals"
|
|
86
102
|
},
|
|
@@ -93,6 +109,10 @@ const unitPhrases = [
|
|
|
93
109
|
next: "nächstes jahr",
|
|
94
110
|
compound: "jahres",
|
|
95
111
|
currentGenitive: "dieses jahres",
|
|
112
|
+
previousGenitive: "letzten jahres",
|
|
113
|
+
nextGenitive: "nächsten jahres",
|
|
114
|
+
indefiniteDative: "einem jahr",
|
|
115
|
+
indefiniteGenitive: "eines jahres",
|
|
96
116
|
dative: "jahren",
|
|
97
117
|
genitive: "des jahres"
|
|
98
118
|
}
|
|
@@ -141,7 +161,125 @@ const currentBoundaryPhrases = unitPhrases.flatMap((entry) => [
|
|
|
141
161
|
entry,
|
|
142
162
|
phrase
|
|
143
163
|
})));
|
|
164
|
+
const declinedBoundaryPeriods = [
|
|
165
|
+
[
|
|
166
|
+
"diese woche",
|
|
167
|
+
"week",
|
|
168
|
+
0,
|
|
169
|
+
"dieser woche",
|
|
170
|
+
"diese woche"
|
|
171
|
+
],
|
|
172
|
+
[
|
|
173
|
+
"letzte woche",
|
|
174
|
+
"week",
|
|
175
|
+
-1,
|
|
176
|
+
"letzter woche",
|
|
177
|
+
"letzte woche"
|
|
178
|
+
],
|
|
179
|
+
[
|
|
180
|
+
"nächste woche",
|
|
181
|
+
"week",
|
|
182
|
+
1,
|
|
183
|
+
"nächster woche",
|
|
184
|
+
"nächste woche"
|
|
185
|
+
],
|
|
186
|
+
[
|
|
187
|
+
"dieser monat",
|
|
188
|
+
"month",
|
|
189
|
+
0,
|
|
190
|
+
"diesem monat",
|
|
191
|
+
"diesen monat"
|
|
192
|
+
],
|
|
193
|
+
[
|
|
194
|
+
"letzter monat",
|
|
195
|
+
"month",
|
|
196
|
+
-1,
|
|
197
|
+
"letztem monat",
|
|
198
|
+
"letzten monat"
|
|
199
|
+
],
|
|
200
|
+
[
|
|
201
|
+
"nächster monat",
|
|
202
|
+
"month",
|
|
203
|
+
1,
|
|
204
|
+
"nächstem monat",
|
|
205
|
+
"nächsten monat"
|
|
206
|
+
],
|
|
207
|
+
[
|
|
208
|
+
"dieses quartal",
|
|
209
|
+
"quarter",
|
|
210
|
+
0,
|
|
211
|
+
"diesem quartal",
|
|
212
|
+
"dieses quartal"
|
|
213
|
+
],
|
|
214
|
+
[
|
|
215
|
+
"letztes quartal",
|
|
216
|
+
"quarter",
|
|
217
|
+
-1,
|
|
218
|
+
"letztem quartal",
|
|
219
|
+
"letztes quartal"
|
|
220
|
+
],
|
|
221
|
+
[
|
|
222
|
+
"nächstes quartal",
|
|
223
|
+
"quarter",
|
|
224
|
+
1,
|
|
225
|
+
"nächstem quartal",
|
|
226
|
+
"nächstes quartal"
|
|
227
|
+
],
|
|
228
|
+
[
|
|
229
|
+
"dieses jahr",
|
|
230
|
+
"year",
|
|
231
|
+
0,
|
|
232
|
+
"diesem jahr",
|
|
233
|
+
"dieses jahr"
|
|
234
|
+
],
|
|
235
|
+
[
|
|
236
|
+
"letztes jahr",
|
|
237
|
+
"year",
|
|
238
|
+
-1,
|
|
239
|
+
"letztem jahr",
|
|
240
|
+
"letztes jahr"
|
|
241
|
+
],
|
|
242
|
+
[
|
|
243
|
+
"nächstes jahr",
|
|
244
|
+
"year",
|
|
245
|
+
1,
|
|
246
|
+
"nächstem jahr",
|
|
247
|
+
"nächstes jahr"
|
|
248
|
+
]
|
|
249
|
+
];
|
|
144
250
|
const relativeAliases = [
|
|
251
|
+
...unitPhrases.flatMap((entry) => [
|
|
252
|
+
[
|
|
253
|
+
entry.currentGenitive,
|
|
254
|
+
entry.unit,
|
|
255
|
+
0,
|
|
256
|
+
entry.current
|
|
257
|
+
],
|
|
258
|
+
[
|
|
259
|
+
entry.previousGenitive,
|
|
260
|
+
entry.unit,
|
|
261
|
+
-1,
|
|
262
|
+
entry.previous
|
|
263
|
+
],
|
|
264
|
+
[
|
|
265
|
+
entry.nextGenitive,
|
|
266
|
+
entry.unit,
|
|
267
|
+
1,
|
|
268
|
+
entry.next
|
|
269
|
+
]
|
|
270
|
+
]),
|
|
271
|
+
...declinedBoundaryPeriods.map(([canonical, unit, direction, dative]) => [
|
|
272
|
+
dative,
|
|
273
|
+
unit,
|
|
274
|
+
direction,
|
|
275
|
+
canonical
|
|
276
|
+
]),
|
|
277
|
+
[
|
|
278
|
+
"vorgestern",
|
|
279
|
+
"day",
|
|
280
|
+
-2,
|
|
281
|
+
"vorgestern"
|
|
282
|
+
],
|
|
145
283
|
[
|
|
146
284
|
"vorletzten tag",
|
|
147
285
|
"day",
|
|
@@ -414,6 +552,20 @@ const monthNumber = (value) => {
|
|
|
414
552
|
const shortIndex = monthAbbreviations.findIndex((aliases) => aliases.some((alias) => alias === normalized));
|
|
415
553
|
return shortIndex === -1 ? void 0 : shortIndex + 1;
|
|
416
554
|
};
|
|
555
|
+
const currentDateLabel = (day, month) => `${day}. ${title(textAt(months, month - 1))}`;
|
|
556
|
+
const withRelativeCase = (canonical, grammaticalCase) => {
|
|
557
|
+
const period = declinedBoundaryPeriods.find((entry) => canonical === canonicalRelative(entry[0]));
|
|
558
|
+
if (period === void 0) return canonical;
|
|
559
|
+
return canonicalRelative(period[grammaticalCase === "dative" ? 3 : 4]);
|
|
560
|
+
};
|
|
561
|
+
const withRelativeGenitive = (canonical) => {
|
|
562
|
+
for (const entry of unitPhrases) {
|
|
563
|
+
if (canonical === canonicalRelative(entry.current)) return canonicalRelative(entry.currentGenitive);
|
|
564
|
+
if (canonical === canonicalRelative(entry.previous)) return canonicalRelative(entry.previousGenitive);
|
|
565
|
+
if (canonical === canonicalRelative(entry.next)) return canonicalRelative(entry.nextGenitive);
|
|
566
|
+
}
|
|
567
|
+
return canonical;
|
|
568
|
+
};
|
|
417
569
|
const parseNamedDate = (input) => {
|
|
418
570
|
const named = String$1.match(/^([0-3]?\d)\.? ([a-zäöüß]+\.?) (\d{4})$/u)(input);
|
|
419
571
|
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 +579,8 @@ const parseNamedDate = (input) => {
|
|
|
427
579
|
if (isIsoDate(value) && value !== "9999-12-31") return Option.some(fixedDatePeriod(value, `${day}. ${title(textAt(months, month - 1))} ${year}`));
|
|
428
580
|
}
|
|
429
581
|
}
|
|
430
|
-
|
|
582
|
+
const current = String$1.match(/^([0-3]?\d)\.? ([a-zäöüß]+\.?)$/u)(input);
|
|
583
|
+
return Option.isSome(current) ? namedCurrentYearDatePeriod(textAt(current.value, 2), textAt(current.value, 1), monthNumber, currentDateLabel) : Option.none();
|
|
431
584
|
};
|
|
432
585
|
const quarterNames = [
|
|
433
586
|
"erstes",
|
|
@@ -472,7 +625,8 @@ const parseQuarter = (input) => {
|
|
|
472
625
|
return quarter === void 0 ? Option.none() : Option.some(quarterOfRelativeYear(quarter, 0, `Q${quarter}`));
|
|
473
626
|
};
|
|
474
627
|
const parseBasePeriod = (input) => {
|
|
475
|
-
|
|
628
|
+
const absoluteDate = absoluteDatePeriod(input, "de");
|
|
629
|
+
if (Option.isSome(absoluteDate)) return absoluteDate;
|
|
476
630
|
const namedDate = parseNamedDate(input);
|
|
477
631
|
if (Option.isSome(namedDate)) return namedDate;
|
|
478
632
|
const quarter = parseQuarter(input);
|
|
@@ -519,12 +673,13 @@ const parsePeriod = (input) => {
|
|
|
519
673
|
const edgeName = textAt(edge.value, 1);
|
|
520
674
|
const period = parseBasePeriod(textAt(edge.value, 2));
|
|
521
675
|
if (Option.isSome(period)) {
|
|
522
|
-
const canonical = `${edgeName === "ende" ? "Ende" : "Anfang"} ${period.value.canonical}`;
|
|
676
|
+
const canonical = `${edgeName === "ende" ? "Ende" : "Anfang"} ${withRelativeGenitive(period.value.canonical)}`;
|
|
523
677
|
return Option.some(edgeName === "ende" ? periodEndDay(period.value, canonical) : periodStartDay(period.value, canonical));
|
|
524
678
|
}
|
|
525
679
|
}
|
|
526
680
|
const wrapper = [
|
|
527
681
|
"im ",
|
|
682
|
+
"am ",
|
|
528
683
|
"für ",
|
|
529
684
|
"während "
|
|
530
685
|
].find((prefix) => input.startsWith(prefix));
|
|
@@ -534,9 +689,9 @@ const currentBoundaryCandidate = (input) => {
|
|
|
534
689
|
const match = currentBoundaryPhrases.find((entry) => entry.phrase === input);
|
|
535
690
|
if (match === void 0) return Option.none();
|
|
536
691
|
const period = relativePeriod(match.entry.unit, 0, match.entry.current);
|
|
537
|
-
return Option.some(periodBoundaryCandidate(period, match.boundary, match.phrase));
|
|
692
|
+
return Option.some(periodBoundaryCandidate(period, match.boundary, canonicalRelative(match.phrase)));
|
|
538
693
|
};
|
|
539
|
-
const boundaryCandidate = (input) => openBoundaryCandidate(input, [
|
|
694
|
+
const boundaryCandidate = (input) => Option.map(openBoundaryCandidate(input, [
|
|
540
695
|
["seit dem beginn von ", "since"],
|
|
541
696
|
["seit dem anfang von ", "since"],
|
|
542
697
|
["seit dem jahr ", "since"],
|
|
@@ -563,12 +718,22 @@ const boundaryCandidate = (input) => openBoundaryCandidate(input, [
|
|
|
563
718
|
["nach ", "after"],
|
|
564
719
|
["bis ", "before"],
|
|
565
720
|
["ab ", "since"]
|
|
566
|
-
], parsePeriod);
|
|
721
|
+
], parsePeriod), (result) => candidate(result.range, canonicalRelative(input)));
|
|
567
722
|
const countedUnit = (value, amount, dative = false) => unitPhrases.find((entry) => {
|
|
568
723
|
if (amount === 1) return value === entry.noun;
|
|
569
724
|
return value === (dative ? entry.dative : entry.plural);
|
|
570
725
|
});
|
|
571
726
|
const parseCalendarOffset = (input) => {
|
|
727
|
+
const singular = String$1.match(/^(vor|in) (einem tag|einer woche|einem monat|einem quartal|einem jahr)$/u)(input);
|
|
728
|
+
if (Option.isSome(singular)) {
|
|
729
|
+
const entry = unitPhrases.find((unit) => unit.indefiniteDative === textAt(singular.value, 2));
|
|
730
|
+
if (entry !== void 0) {
|
|
731
|
+
const isPast = textAt(singular.value, 1) === "vor";
|
|
732
|
+
const direction = isPast ? -1 : 1;
|
|
733
|
+
const canonical = `${isPast ? "vor" : "in"} ${canonicalRelative(entry.indefiniteDative)}`;
|
|
734
|
+
return Option.some(candidate(periodRange(relativePeriod(entry.unit, direction, canonical)), canonical));
|
|
735
|
+
}
|
|
736
|
+
}
|
|
572
737
|
const prefixed = String$1.match(/^(vor|in) ([1-9]\d*) (tag|tagen|woche|wochen|monat|monaten|quartal|quartalen|jahr|jahren)$/u)(input);
|
|
573
738
|
const prior = String$1.match(/^([1-9]\d*) (tag|tage|woche|wochen|monat|monate|quartal|quartale|jahr|jahre) zuvor$/u)(input);
|
|
574
739
|
const match = Option.firstSomeOf([prefixed, prior]);
|
|
@@ -588,30 +753,51 @@ const parseCalendarOffset = (input) => {
|
|
|
588
753
|
};
|
|
589
754
|
const parseRollingPeriod = (input) => {
|
|
590
755
|
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);
|
|
756
|
+
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);
|
|
757
|
+
const singularSince = String$1.match(/^seit (einem tag|einer woche|einem monat|einem quartal|einem jahr)$/u)(input);
|
|
592
758
|
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
759
|
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
760
|
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);
|
|
761
|
+
const singularFuture = String$1.match(/^innerhalb (eines tages|einer woche|eines monats|eines quartals|eines jahres)$/u)(input);
|
|
595
762
|
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
763
|
const match = Option.firstSomeOf([
|
|
597
764
|
past,
|
|
598
765
|
pastDative,
|
|
766
|
+
singularSince,
|
|
599
767
|
pastGenitive,
|
|
600
768
|
future,
|
|
601
769
|
futureDative,
|
|
770
|
+
singularFuture,
|
|
602
771
|
futureGenitive
|
|
603
772
|
]);
|
|
604
773
|
if (Option.isNone(match)) return Option.none();
|
|
605
|
-
const
|
|
774
|
+
const isSingular = Option.isSome(singularSince) || Option.isSome(singularFuture);
|
|
775
|
+
const amount = parseTrailingCount(isSingular ? "1" : textAt(match.value, 1));
|
|
606
776
|
if (Option.isNone(amount)) return Option.none();
|
|
607
|
-
const usesDative = Option.isSome(pastDative) || Option.isSome(futureDative);
|
|
608
|
-
const
|
|
777
|
+
const usesDative = Option.isSome(pastDative) || Option.isSome(singularSince) || Option.isSome(futureDative);
|
|
778
|
+
const sinceEntry = Option.isSome(singularSince) ? unitPhrases.find((unit) => unit.indefiniteDative === textAt(match.value, 1)) : void 0;
|
|
779
|
+
const futureEntry = Option.isSome(singularFuture) ? unitPhrases.find((unit) => unit.indefiniteGenitive === textAt(match.value, 1)) : void 0;
|
|
780
|
+
const countedEntry = isSingular ? void 0 : countedUnit(textAt(match.value, 2), amount.value, usesDative);
|
|
781
|
+
const entry = sinceEntry ?? futureEntry ?? countedEntry;
|
|
609
782
|
if (entry === void 0) return Option.none();
|
|
610
|
-
const isFuture = Option.isSome(future) || Option.isSome(futureDative) || Option.isSome(futureGenitive);
|
|
783
|
+
const isFuture = Option.isSome(future) || Option.isSome(futureDative) || Option.isSome(singularFuture) || Option.isSome(futureGenitive);
|
|
611
784
|
const range = isFuture ? futureRange(amount.value, entry.unit) : trailingRange(amount.value, entry.unit);
|
|
785
|
+
if (amount.value === 1) {
|
|
786
|
+
const phrase = isFuture ? `innerhalb ${canonicalRelative(entry.indefiniteGenitive)}` : `seit ${canonicalRelative(entry.indefiniteDative)}`;
|
|
787
|
+
return Option.some(candidate(range, phrase));
|
|
788
|
+
}
|
|
612
789
|
const direction = isFuture ? "nächste" : "letzte";
|
|
613
|
-
|
|
614
|
-
|
|
790
|
+
return Option.some(candidate(range, `${direction} ${amount.value} ${title(entry.plural)}`));
|
|
791
|
+
};
|
|
792
|
+
const parseElidedDateRange = (input) => {
|
|
793
|
+
const match = String$1.match(/^(?:vom )?([0-3]?\d)\.(?: bis |[–—-])(?:zum )?([0-3]?\d)\.? ([a-zäöüß]+\.?)(?: (\d{4}))?$/u)(input);
|
|
794
|
+
if (Option.isNone(match)) return Option.none();
|
|
795
|
+
const lowerDay = textAt(match.value, 1);
|
|
796
|
+
const upperDay = textAt(match.value, 2);
|
|
797
|
+
const month = textAt(match.value, 3);
|
|
798
|
+
const year = textAt(match.value, 4);
|
|
799
|
+
const suffix = year.length === 0 ? "" : ` ${year}`;
|
|
800
|
+
return joinedPeriodCandidate(`von ${lowerDay}. ${month}${suffix} bis ${upperDay}. ${month}${suffix}`, [["von ", " bis "]], parsePeriod, (lower, upper) => `von ${lower} bis ${upper}`);
|
|
615
801
|
};
|
|
616
802
|
const parseGerman = (input) => {
|
|
617
803
|
const remaining = remainingPeriodPhrases.find((entry) => entry.phrase === input);
|
|
@@ -626,6 +812,8 @@ const parseGerman = (input) => {
|
|
|
626
812
|
if (toDate !== void 0) return Option.some(candidate(periodToDateRange(toDate.entry.unit), canonicalToDate(toDate.entry)));
|
|
627
813
|
const currentBoundary = currentBoundaryCandidate(input);
|
|
628
814
|
if (Option.isSome(currentBoundary)) return currentBoundary;
|
|
815
|
+
const elided = parseElidedDateRange(input);
|
|
816
|
+
if (Option.isSome(elided)) return elided;
|
|
629
817
|
const nowBounded = joinedNowCandidate(input, [
|
|
630
818
|
["von ", " bis heute"],
|
|
631
819
|
["vom ", " bis heute"],
|
|
@@ -657,9 +845,10 @@ const parseGerman = (input) => {
|
|
|
657
845
|
if (Option.isSome(bounded)) return bounded;
|
|
658
846
|
const boundary = boundaryCandidate(input);
|
|
659
847
|
if (Option.isSome(boundary)) return boundary;
|
|
660
|
-
return Option.map(
|
|
848
|
+
return parsePeriod(input).pipe(Option.map((period) => candidate(periodRange(period), period.canonical)));
|
|
661
849
|
};
|
|
662
850
|
const staticPeriodPhrases = [
|
|
851
|
+
"vorgestern",
|
|
663
852
|
"dieses wochenende",
|
|
664
853
|
"letztes wochenende",
|
|
665
854
|
"nächstes wochenende",
|
|
@@ -669,12 +858,12 @@ const staticPeriodPhrases = [
|
|
|
669
858
|
entry.current,
|
|
670
859
|
entry.previous,
|
|
671
860
|
entry.next,
|
|
672
|
-
`anfang ${entry.
|
|
673
|
-
`ende ${entry.
|
|
674
|
-
`anfang ${entry.
|
|
675
|
-
`ende ${entry.
|
|
676
|
-
`anfang ${entry.
|
|
677
|
-
`ende ${entry.
|
|
861
|
+
`anfang ${entry.currentGenitive}`,
|
|
862
|
+
`ende ${entry.currentGenitive}`,
|
|
863
|
+
`anfang ${entry.previousGenitive}`,
|
|
864
|
+
`ende ${entry.previousGenitive}`,
|
|
865
|
+
`anfang ${entry.nextGenitive}`,
|
|
866
|
+
`ende ${entry.nextGenitive}`
|
|
678
867
|
]),
|
|
679
868
|
...[
|
|
680
869
|
1,
|
|
@@ -719,8 +908,21 @@ const countedSuggestions = (input) => {
|
|
|
719
908
|
const germanSuggestionPhrases = [
|
|
720
909
|
...unitPhrases.map(canonicalToDate),
|
|
721
910
|
...unitPhrases.map((entry) => `rest ${entry.genitive}`),
|
|
911
|
+
...unitPhrases.flatMap((entry) => [
|
|
912
|
+
`seit ${entry.indefiniteDative}`,
|
|
913
|
+
`innerhalb ${entry.indefiniteGenitive}`,
|
|
914
|
+
`vor ${entry.indefiniteDative}`,
|
|
915
|
+
`in ${entry.indefiniteDative}`
|
|
916
|
+
]),
|
|
722
917
|
...staticPeriodPhrases,
|
|
723
|
-
...
|
|
918
|
+
...currentBoundaryPhrases.map((entry) => entry.phrase),
|
|
919
|
+
...declinedBoundaryPeriods.flatMap(([, , , dative, accusative]) => [
|
|
920
|
+
`seit ${dative}`,
|
|
921
|
+
`vor ${dative}`,
|
|
922
|
+
`bis einschließlich ${accusative}`,
|
|
923
|
+
`nach ${dative}`
|
|
924
|
+
]),
|
|
925
|
+
...prefixNaturalPhrases(months, boundaryPrefixes),
|
|
724
926
|
"bis heute",
|
|
725
927
|
"ab jetzt"
|
|
726
928
|
].map((phrase) => normalizeNaturalText(phrase, "de"));
|
|
@@ -742,15 +944,20 @@ const renderGerman = (range) => {
|
|
|
742
944
|
const future = futurePeriod(range);
|
|
743
945
|
if (Option.isSome(future)) {
|
|
744
946
|
const entry = unitPhrases.find((unit) => unit.unit === future.value.unit);
|
|
745
|
-
if (entry !== void 0) return Option.some(
|
|
947
|
+
if (entry !== void 0) return Option.some(future.value.amount === 1 ? `innerhalb ${canonicalRelative(entry.indefiniteGenitive)}` : `nächste ${future.value.amount} ${title(entry.plural)}`);
|
|
746
948
|
}
|
|
747
949
|
const trailing = trailingPeriod(range);
|
|
748
950
|
if (Option.isSome(trailing)) {
|
|
749
951
|
const entry = unitPhrases.find((unit) => unit.unit === trailing.value.unit);
|
|
750
|
-
if (entry !== void 0) return Option.some(trailing.value.amount === 1 ? `
|
|
952
|
+
if (entry !== void 0) return Option.some(trailing.value.amount === 1 ? `seit ${canonicalRelative(entry.indefiniteDative)}` : `letzte ${trailing.value.amount} ${title(entry.plural)}`);
|
|
751
953
|
}
|
|
752
|
-
const
|
|
753
|
-
|
|
954
|
+
const dated = [...datedPeriods(range, months), ...datedQuarterPeriods(range)];
|
|
955
|
+
const periods = [
|
|
956
|
+
...staticPeriods,
|
|
957
|
+
...currentYearDatePeriods(range, currentDateLabel),
|
|
958
|
+
...periodsFromPhrases([...dated, ...dated.flatMap((phrase) => [`anfang ${phrase}`, `ende ${phrase}`])], parsePeriod)
|
|
959
|
+
];
|
|
960
|
+
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
961
|
};
|
|
755
962
|
const GermanContribution = new BaseLanguageContribution({
|
|
756
963
|
locale: "de",
|
|
@@ -770,7 +977,11 @@ const GermanContribution = new BaseLanguageContribution({
|
|
|
770
977
|
entry.noun,
|
|
771
978
|
entry.plural,
|
|
772
979
|
entry.dative,
|
|
980
|
+
...entry.indefiniteDative.split(" "),
|
|
981
|
+
...entry.indefiniteGenitive.split(" "),
|
|
773
982
|
...entry.currentGenitive.split(" "),
|
|
983
|
+
...entry.previousGenitive.split(" "),
|
|
984
|
+
...entry.nextGenitive.split(" "),
|
|
774
985
|
...entry.current.split(" "),
|
|
775
986
|
...entry.previous.split(" "),
|
|
776
987
|
...entry.next.split(" ")
|
package/dist/locales/en.mjs
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { isIsoDate } from "../ast/schemas.mjs";
|
|
2
1
|
import { BaseLanguageContribution } from "../language/model.mjs";
|
|
3
2
|
import { normalizeNaturalText } from "../natural/text.mjs";
|
|
4
3
|
import { defineLanguagePlugin, languagePluginsLayer } from "../language/registry.mjs";
|
|
5
4
|
import { correctWhitespaceSeparatedText } from "../natural/correction.mjs";
|
|
6
5
|
import { completeNaturalPhrases, fixedCalendarPeriodPhrases, naturalCount, prefixNaturalPhrases } from "../natural/suggestion.mjs";
|
|
7
|
-
import { calendarPeriodOffset, candidate, datedPeriods, datedQuarterPeriods,
|
|
6
|
+
import { absoluteDatePeriod, calendarPeriodOffset, candidate, currentYearDatePeriods, datedPeriods, datedQuarterPeriods, fixedMonthPeriod, fixedQuarterPeriod, fixedYearPeriod, fromNowRange, futurePeriod, futureRange, joinedNowCandidate, joinedPeriodCandidate, monthOfRelativeYear, namedCurrentYearDatePeriod, namedDatePeriod, openBoundaryCandidate, parseTrailingCount, periodEndDay, periodPreviousDay, periodRange, periodStartDay, periodToDateRange, periodsFromPhrases, quarterOfRelativeYear, relativePeriod, relativeWeekend, remainingPeriodRange, renderPeriodRange, textAt, trailingPeriod, trailingRange, untilNowRange, validYear } from "./shared.mjs";
|
|
8
7
|
import { Effect, Option, String as String$1 } from "effect";
|
|
9
8
|
//#region src/locales/en.ts
|
|
10
9
|
const months = [
|
|
@@ -159,15 +158,21 @@ const parseQuarter = (input) => {
|
|
|
159
158
|
const quarter = quarterNumber(textAt(standaloneMatch.value, 1));
|
|
160
159
|
return quarter === void 0 ? Option.none() : Option.some(quarterOfRelativeYear(quarter, 0, `Q${quarter}`));
|
|
161
160
|
};
|
|
161
|
+
const currentDateLabel = (day, month) => `${day} ${title(textAt(months, month - 1))}`;
|
|
162
162
|
const parseNamedDate = (input) => {
|
|
163
|
-
const dayFirst = String$1.match(/^([0-3]?\d)(?:st|nd|rd|th)? ([a-z]+\.?) (\d{4})$/u)(input);
|
|
163
|
+
const dayFirst = String$1.match(/^(?:the )?([0-3]?\d)(?:st|nd|rd|th)?(?: of)? ([a-z]+\.?),? (\d{4})$/u)(input);
|
|
164
164
|
if (Option.isSome(dayFirst)) return namedDatePeriod(textAt(dayFirst.value, 3), textAt(dayFirst.value, 2), textAt(dayFirst.value, 1), monthNumber, (day, month, year) => `${day} ${title(textAt(months, month - 1))} ${year}`);
|
|
165
|
-
const monthFirst = String$1.match(/^([a-z]+\.?) ([0-3]?\d)(?:st|nd|rd|th)?,? (\d{4})$/u)(input);
|
|
165
|
+
const monthFirst = String$1.match(/^([a-z]+\.?) (?:the )?([0-3]?\d)(?:st|nd|rd|th)?,? (\d{4})$/u)(input);
|
|
166
166
|
if (Option.isSome(monthFirst)) return namedDatePeriod(textAt(monthFirst.value, 3), textAt(monthFirst.value, 1), textAt(monthFirst.value, 2), monthNumber, (day, month, year) => `${day} ${title(textAt(months, month - 1))} ${year}`);
|
|
167
|
-
|
|
167
|
+
const currentDayFirst = String$1.match(/^(?:the )?([0-3]?\d)(?:st|nd|rd|th)?(?: of)? ([a-z]+\.?)$/u)(input);
|
|
168
|
+
const currentMonthFirst = String$1.match(/^([a-z]+\.?) (?:the )?([0-3]?\d)(?:st|nd|rd|th)?$/u)(input);
|
|
169
|
+
const current = Option.firstSomeOf([currentDayFirst, currentMonthFirst]);
|
|
170
|
+
if (Option.isNone(current)) return Option.none();
|
|
171
|
+
return Option.isSome(currentDayFirst) ? namedCurrentYearDatePeriod(textAt(current.value, 2), textAt(current.value, 1), monthNumber, currentDateLabel) : namedCurrentYearDatePeriod(textAt(current.value, 1), textAt(current.value, 2), monthNumber, currentDateLabel);
|
|
168
172
|
};
|
|
169
173
|
const parseBasePeriod = (input) => {
|
|
170
|
-
|
|
174
|
+
const absoluteDate = absoluteDatePeriod(input, "en");
|
|
175
|
+
if (Option.isSome(absoluteDate)) return absoluteDate;
|
|
171
176
|
const namedDate = parseNamedDate(input);
|
|
172
177
|
if (Option.isSome(namedDate)) return namedDate;
|
|
173
178
|
const quarter = parseQuarter(input);
|
|
@@ -213,7 +218,7 @@ const parseBasePeriod = (input) => {
|
|
|
213
218
|
return Option.some(relativePeriod(entry[1], direction, input));
|
|
214
219
|
}
|
|
215
220
|
}
|
|
216
|
-
const relative = String$1.match(/^(?:the )?(last|previous|this|current|next|coming|upcoming) ([a-z]+)$/u)(input);
|
|
221
|
+
const relative = String$1.match(/^(?:the )?(last|previous|this|current|next|coming|upcoming) (?:calendar )?([a-z]+)$/u)(input);
|
|
217
222
|
if (Option.isSome(relative)) {
|
|
218
223
|
const unitText = textAt(relative.value, 2);
|
|
219
224
|
const unit = units.find((entry) => entry[0] === unitText)?.[1];
|
|
@@ -226,6 +231,11 @@ const parseBasePeriod = (input) => {
|
|
|
226
231
|
return Option.none();
|
|
227
232
|
};
|
|
228
233
|
const parsePeriod = (input) => {
|
|
234
|
+
const previousDay = String$1.match(/^(?:the )?day before (.+)$/u)(input);
|
|
235
|
+
if (Option.isSome(previousDay)) {
|
|
236
|
+
const period = parseBasePeriod(textAt(previousDay.value, 1));
|
|
237
|
+
if (Option.isSome(period)) return Option.some(periodPreviousDay(period.value, `the day before ${period.value.canonical}`));
|
|
238
|
+
}
|
|
229
239
|
const edge = String$1.match(/^(?:the )?(start|beginning|end) of (.+)$/u)(input);
|
|
230
240
|
if (Option.isSome(edge)) {
|
|
231
241
|
const period = parseBasePeriod(textAt(edge.value, 2));
|
|
@@ -247,6 +257,8 @@ const parsePeriod = (input) => {
|
|
|
247
257
|
};
|
|
248
258
|
const boundaryCandidate = (input) => openBoundaryCandidate(input, [
|
|
249
259
|
["up to and including ", "through"],
|
|
260
|
+
["on or before ", "through"],
|
|
261
|
+
["on or after ", "since"],
|
|
250
262
|
["up to including ", "through"],
|
|
251
263
|
["since the beginning of ", "since"],
|
|
252
264
|
["since the start of ", "since"],
|
|
@@ -263,6 +275,8 @@ const boundaryCandidate = (input) => openBoundaryCandidate(input, [
|
|
|
263
275
|
["before start of ", "before"],
|
|
264
276
|
["starting ", "since"],
|
|
265
277
|
["through ", "through"],
|
|
278
|
+
["earlier than ", "before"],
|
|
279
|
+
["later than ", "after"],
|
|
266
280
|
["before ", "before"],
|
|
267
281
|
["since ", "since"],
|
|
268
282
|
["after ", "after"],
|
|
@@ -305,22 +319,47 @@ const parseCalendarOffset = (input) => {
|
|
|
305
319
|
};
|
|
306
320
|
const parseRollingPeriod = (input) => {
|
|
307
321
|
const past = String$1.match(/^(?:(?:last|previous|past) |(?:in|over) the (?:last|previous|past) )?([1-9]\d*) (day|days|week|weeks|month|months|quarter|quarters|year|years)$/u)(input);
|
|
322
|
+
const singularPast = String$1.match(/^(?:past|the past|in the past|over the past|this past) (day|week|month|quarter|year)$/u)(input);
|
|
308
323
|
const future = String$1.match(/^(?:(?:next|coming|within) |(?:in|over|within) the (?:next|coming) )([1-9]\d*) (day|days|week|weeks|month|months|quarter|quarters|year|years)$/u)(input);
|
|
309
|
-
const
|
|
324
|
+
const singularFuture = String$1.match(/^(?:in|over|within) the (?:next|coming) (day|week|month|quarter|year)$/u)(input);
|
|
325
|
+
const match = Option.firstSomeOf([
|
|
326
|
+
past,
|
|
327
|
+
singularPast,
|
|
328
|
+
future,
|
|
329
|
+
singularFuture
|
|
330
|
+
]);
|
|
310
331
|
if (Option.isNone(match)) return Option.none();
|
|
311
|
-
const
|
|
332
|
+
const singular = Option.isSome(singularPast) || Option.isSome(singularFuture);
|
|
333
|
+
const amount = parseTrailingCount(singular ? "1" : textAt(match.value, 1));
|
|
312
334
|
if (Option.isNone(amount)) return Option.none();
|
|
313
|
-
const
|
|
335
|
+
const unitText = textAt(match.value, singular ? 1 : 2);
|
|
336
|
+
const entry = countedUnit(unitText, amount.value);
|
|
314
337
|
if (entry === void 0) return Option.none();
|
|
315
|
-
const isFuture = Option.isSome(future);
|
|
338
|
+
const isFuture = Option.isSome(future) || Option.isSome(singularFuture);
|
|
316
339
|
const range = isFuture ? futureRange(amount.value, entry[1]) : trailingRange(amount.value, entry[1]);
|
|
340
|
+
if (amount.value === 1) {
|
|
341
|
+
const canonical = isFuture ? `within the next ${entry[0]}` : `past ${entry[0]}`;
|
|
342
|
+
return Option.some(candidate(range, canonical));
|
|
343
|
+
}
|
|
317
344
|
const direction = isFuture ? "next" : "last";
|
|
318
|
-
|
|
319
|
-
|
|
345
|
+
return Option.some(candidate(range, `${direction} ${amount.value} ${entry[2]}`));
|
|
346
|
+
};
|
|
347
|
+
const parseElidedDateRange = (input) => {
|
|
348
|
+
const monthFirst = String$1.match(/^([a-z]+\.?) ([0-3]?\d)(?:st|nd|rd|th)?(?: (?:to|through) |[–—-])([0-3]?\d)(?:st|nd|rd|th)?,?(?: (\d{4}))?$/u)(input);
|
|
349
|
+
const dayFirst = String$1.match(/^([0-3]?\d)(?:st|nd|rd|th)?(?: (?:to|through) |[–—-])([0-3]?\d)(?:st|nd|rd|th)? ([a-z]+\.?)(?: (\d{4}))?$/u)(input);
|
|
350
|
+
const match = Option.firstSomeOf([monthFirst, dayFirst]);
|
|
351
|
+
if (Option.isNone(match)) return Option.none();
|
|
352
|
+
const isMonthFirst = Option.isSome(monthFirst);
|
|
353
|
+
const month = textAt(match.value, isMonthFirst ? 1 : 3);
|
|
354
|
+
const lowerDay = textAt(match.value, isMonthFirst ? 2 : 1);
|
|
355
|
+
const upperDay = textAt(match.value, isMonthFirst ? 3 : 2);
|
|
356
|
+
const year = textAt(match.value, 4);
|
|
357
|
+
const suffix = year.length === 0 ? "" : ` ${year}`;
|
|
358
|
+
return joinedPeriodCandidate(`from ${lowerDay} ${month}${suffix} to ${upperDay} ${month}${suffix}`, [["from ", " to "]], parsePeriod, (lower, upper) => `from ${lower} to ${upper}`);
|
|
320
359
|
};
|
|
321
360
|
const parseEnglish = (input) => {
|
|
322
361
|
const remaining = remainingPeriodPhrases.find((entry) => entry.phrase === input);
|
|
323
|
-
if (remaining !== void 0) return Option.some(candidate(remainingPeriodRange(remaining.entry[1]), `rest of ${remaining.entry[0]}`));
|
|
362
|
+
if (remaining !== void 0) return Option.some(candidate(remainingPeriodRange(remaining.entry[1]), `rest of the ${remaining.entry[0]}`));
|
|
324
363
|
if ([
|
|
325
364
|
"until now",
|
|
326
365
|
"till now",
|
|
@@ -338,10 +377,13 @@ const parseEnglish = (input) => {
|
|
|
338
377
|
if (Option.isSome(rolling)) return rolling;
|
|
339
378
|
const toDate = toDatePhrases.find((entry) => entry.phrase === input);
|
|
340
379
|
if (toDate !== void 0) return Option.some(candidate(periodToDateRange(toDate.entry[1]), `${toDate.entry[0]} to date`));
|
|
380
|
+
const elided = parseElidedDateRange(input);
|
|
381
|
+
if (Option.isSome(elided)) return elided;
|
|
341
382
|
const nowBounded = joinedNowCandidate(input, [
|
|
342
383
|
["from ", " to now"],
|
|
343
384
|
["from ", " until now"],
|
|
344
385
|
["from ", " through now"],
|
|
386
|
+
["from ", " to date"],
|
|
345
387
|
["between ", " and now"]
|
|
346
388
|
], [
|
|
347
389
|
"from now to ",
|
|
@@ -380,18 +422,19 @@ const parseEnglish = (input) => {
|
|
|
380
422
|
if (Option.isSome(bounded)) return bounded;
|
|
381
423
|
const boundary = boundaryCandidate(input);
|
|
382
424
|
if (Option.isSome(boundary)) return boundary;
|
|
383
|
-
return Option.map(
|
|
425
|
+
return parsePeriod(input).pipe(Option.map((period) => candidate(periodRange(period), period.canonical)));
|
|
384
426
|
};
|
|
385
427
|
const staticPeriodPhrases = [
|
|
386
428
|
"today",
|
|
387
429
|
"yesterday",
|
|
430
|
+
"the day before yesterday",
|
|
388
431
|
"tomorrow",
|
|
389
432
|
"this weekend",
|
|
390
433
|
"last weekend",
|
|
391
434
|
"next weekend",
|
|
392
435
|
"the weekend before last",
|
|
393
436
|
"the weekend after next",
|
|
394
|
-
...units.flatMap((entry) => [
|
|
437
|
+
...units.filter((entry) => entry[1] !== "day").flatMap((entry) => [
|
|
395
438
|
`this ${entry[0]}`,
|
|
396
439
|
`last ${entry[0]}`,
|
|
397
440
|
`next ${entry[0]}`,
|
|
@@ -441,7 +484,8 @@ const countedSuggestions = (input) => {
|
|
|
441
484
|
};
|
|
442
485
|
const englishSuggestionPhrases = [
|
|
443
486
|
...units.map((entry) => `${entry[0]} to date`),
|
|
444
|
-
...
|
|
487
|
+
...remainingPeriodPhrases.map((entry) => entry.phrase),
|
|
488
|
+
...units.flatMap((entry) => [`past ${entry[0]}`, `within the next ${entry[0]}`]),
|
|
445
489
|
...staticPeriodPhrases,
|
|
446
490
|
...prefixNaturalPhrases(staticPeriodPhrases, boundaryPrefixes),
|
|
447
491
|
"until now",
|
|
@@ -468,15 +512,19 @@ const renderEnglish = (range) => {
|
|
|
468
512
|
const future = futurePeriod(range);
|
|
469
513
|
if (Option.isSome(future)) {
|
|
470
514
|
const entry = units.find((unit) => unit[1] === future.value.unit);
|
|
471
|
-
if (entry !== void 0) return Option.some(
|
|
515
|
+
if (entry !== void 0) return Option.some(future.value.amount === 1 ? `within the next ${entry[0]}` : `next ${future.value.amount} ${entry[2]}`);
|
|
472
516
|
}
|
|
473
517
|
const trailing = trailingPeriod(range);
|
|
474
518
|
if (Option.isSome(trailing)) {
|
|
475
519
|
const entry = units.find((unit) => unit[1] === trailing.value.unit);
|
|
476
|
-
if (entry !== void 0) return Option.some(trailing.value.amount === 1 ? `
|
|
520
|
+
if (entry !== void 0) return Option.some(trailing.value.amount === 1 ? `past ${entry[0]}` : `last ${trailing.value.amount} ${entry[2]}`);
|
|
477
521
|
}
|
|
478
|
-
const periods = [
|
|
479
|
-
|
|
522
|
+
const periods = [
|
|
523
|
+
...staticPeriods,
|
|
524
|
+
...currentYearDatePeriods(range, currentDateLabel),
|
|
525
|
+
...periodsFromPhrases([...datedPeriods(range, months), ...datedQuarterPeriods(range)], parsePeriod)
|
|
526
|
+
];
|
|
527
|
+
return renderPeriodRange(range, [...units.map((entry) => candidate(periodToDateRange(entry[1]), `${entry[0]} to date`)), ...units.map((entry) => candidate(remainingPeriodRange(entry[1]), `rest of the ${entry[0]}`))], periods, (period) => `since ${period}`, (period) => `before ${period}`, (period) => `through ${period}`, (period) => `after ${period}`, (lower, upper) => `from ${lower} to ${upper}`, (period) => `from ${period} to now`, (period) => `from now to ${period}`, () => "until now", () => "from now");
|
|
480
528
|
};
|
|
481
529
|
const EnglishContribution = new BaseLanguageContribution({
|
|
482
530
|
locale: "en",
|