chronolizer 0.3.0 → 0.4.1
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 +50 -47
- package/dist/ast/fold.mjs +8 -10
- package/dist/ast/normalize.mjs +7 -16
- package/dist/filter/codec.mjs +5 -6
- package/dist/filter/expression.mjs +26 -21
- package/dist/language/model.mjs +1 -1
- package/dist/language/registry.mjs +34 -12
- package/dist/locales/cs.mjs +194 -36
- package/dist/locales/de.mjs +307 -490
- package/dist/locales/en.mjs +162 -46
- package/dist/locales/es.mjs +187 -37
- package/dist/locales/fr.mjs +168 -52
- package/dist/locales/nl.mjs +125 -37
- package/dist/locales/pl.mjs +163 -36
- package/dist/locales/shared.mjs +68 -5
- package/dist/locales/tr.mjs +144 -42
- package/dist/natural/correction.d.mts +1 -1
- package/dist/natural/correction.mjs +97 -28
- package/dist/natural/format.mjs +1 -1
- package/dist/natural/parse.mjs +6 -5
- package/dist/natural/suggest.mjs +27 -3
- package/dist/natural/suggestion.mjs +50 -26
- package/dist/resolve/resolve.mjs +2 -3
- package/package.json +2 -1
package/dist/locales/en.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { normalizeNaturalText } from "../natural/text.mjs";
|
|
|
3
3
|
import { defineLanguagePlugin, languagePluginsLayer } from "../language/registry.mjs";
|
|
4
4
|
import { correctWhitespaceSeparatedText } from "../natural/correction.mjs";
|
|
5
5
|
import { completeNaturalPhrases, fixedCalendarPeriodPhrases, naturalCount, prefixNaturalPhrases } from "../natural/suggestion.mjs";
|
|
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";
|
|
6
|
+
import { absoluteDatePeriod, calendarPeriodOffset, candidate, compileCountAliasNormalizer, compoundCountAliases, countAliasVocabulary, currentYearDatePeriods, datedPeriods, datedQuarterPeriods, decimalTens, decomposeShiftedPeriodRange, fixedMonthPeriod, fixedQuarterPeriod, fixedYearPeriod, fromNowRange, futurePeriod, futureRange, joinedNowCandidate, joinedPeriodCandidate, monthOfRelativeYear, namedCurrentYearDatePeriod, namedDatePeriod, openBoundaryCandidate, parseTrailingCount, periodEndDay, periodPreviousDay, periodRange, periodStartDay, periodToDateRange, periodsFromPhrases, quarterOfRelativeYear, relativePeriod, relativeWeekday, relativeWeekend, remainingPeriodRange, renderPeriodRange, sequentialCountAliases, shiftPeriod, textAt, trailingPeriod, trailingRange, untilNowRange, validYear } from "./shared.mjs";
|
|
7
7
|
import { Effect, Option, String as String$1 } from "effect";
|
|
8
8
|
//#region src/locales/en.ts
|
|
9
9
|
const months = [
|
|
@@ -34,12 +34,59 @@ const monthAbbreviations = [
|
|
|
34
34
|
["nov"],
|
|
35
35
|
["dec"]
|
|
36
36
|
];
|
|
37
|
+
const weekdays = [
|
|
38
|
+
"monday",
|
|
39
|
+
"tuesday",
|
|
40
|
+
"wednesday",
|
|
41
|
+
"thursday",
|
|
42
|
+
"friday",
|
|
43
|
+
"saturday",
|
|
44
|
+
"sunday"
|
|
45
|
+
];
|
|
46
|
+
const nextWeekdayPhrases = weekdays.map((weekday) => `next ${weekday}`);
|
|
37
47
|
const quarterNames = [
|
|
38
48
|
"first",
|
|
39
49
|
"second",
|
|
40
50
|
"third",
|
|
41
51
|
"fourth"
|
|
42
52
|
];
|
|
53
|
+
const englishCountWords = [
|
|
54
|
+
"one",
|
|
55
|
+
"two",
|
|
56
|
+
"three",
|
|
57
|
+
"four",
|
|
58
|
+
"five",
|
|
59
|
+
"six",
|
|
60
|
+
"seven",
|
|
61
|
+
"eight",
|
|
62
|
+
"nine",
|
|
63
|
+
"ten",
|
|
64
|
+
"eleven",
|
|
65
|
+
"twelve",
|
|
66
|
+
"thirteen",
|
|
67
|
+
"fourteen",
|
|
68
|
+
"fifteen",
|
|
69
|
+
"sixteen",
|
|
70
|
+
"seventeen",
|
|
71
|
+
"eighteen",
|
|
72
|
+
"nineteen",
|
|
73
|
+
"twenty"
|
|
74
|
+
];
|
|
75
|
+
const englishCountOnes = englishCountWords.slice(0, 9).map((word, index) => [index + 1, word]);
|
|
76
|
+
const englishCountAliases = [...sequentialCountAliases(englishCountWords.map((word) => [word]), 1), ...compoundCountAliases(decimalTens([
|
|
77
|
+
"twenty",
|
|
78
|
+
"thirty",
|
|
79
|
+
"forty",
|
|
80
|
+
"fifty",
|
|
81
|
+
"sixty",
|
|
82
|
+
"seventy",
|
|
83
|
+
"eighty",
|
|
84
|
+
"ninety"
|
|
85
|
+
]), englishCountOnes, (ten, one) => [`${ten} ${one}`, `${ten}-${one}`])];
|
|
86
|
+
const normalizeEnglishCounts = compileCountAliasNormalizer(englishCountAliases);
|
|
87
|
+
const englishCountVocabulary = new Set(countAliasVocabulary(englishCountAliases));
|
|
88
|
+
const correctEnglish = (input, vocabulary) => correctWhitespaceSeparatedText(input, vocabulary, englishCountVocabulary);
|
|
89
|
+
const normalizeEnglish = (input, locale) => normalizeEnglishCounts(normalizeNaturalText(input, locale));
|
|
43
90
|
const units = [
|
|
44
91
|
[
|
|
45
92
|
"day",
|
|
@@ -170,13 +217,13 @@ const parseNamedDate = (input) => {
|
|
|
170
217
|
if (Option.isNone(current)) return Option.none();
|
|
171
218
|
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);
|
|
172
219
|
};
|
|
173
|
-
const
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (Option.isSome(
|
|
220
|
+
const parseDatedPeriod = (input) => {
|
|
221
|
+
const knownPeriod = Option.firstSomeOf([
|
|
222
|
+
absoluteDatePeriod(input, "en"),
|
|
223
|
+
parseNamedDate(input),
|
|
224
|
+
parseQuarter(input)
|
|
225
|
+
]);
|
|
226
|
+
if (Option.isSome(knownPeriod)) return knownPeriod;
|
|
180
227
|
const yearMatch = String$1.match(/^(?:(?:the )?(?:calendar )?year )?(\d{4})$/u)(input);
|
|
181
228
|
if (Option.isSome(yearMatch)) {
|
|
182
229
|
const year = validYear(textAt(yearMatch.value, 1));
|
|
@@ -188,6 +235,15 @@ const parseBasePeriod = (input) => {
|
|
|
188
235
|
const year = validYear(textAt(monthYear.value, 2));
|
|
189
236
|
if (month !== void 0 && year !== void 0) return Option.some(fixedMonthPeriod(year, month, `${title(textAt(months, month - 1))} ${year}`));
|
|
190
237
|
}
|
|
238
|
+
return Option.none();
|
|
239
|
+
};
|
|
240
|
+
const parseRelativeMonth = (input) => {
|
|
241
|
+
const prefixedRelativeMonth = String$1.match(/^(last|this|next) ([a-z]+\.?)$/u)(input);
|
|
242
|
+
if (Option.isSome(prefixedRelativeMonth)) {
|
|
243
|
+
const directionText = textAt(prefixedRelativeMonth.value, 1);
|
|
244
|
+
const month = monthNumber(textAt(prefixedRelativeMonth.value, 2));
|
|
245
|
+
if (month !== void 0) return Option.some(monthOfRelativeYear(month, relativeDirection(directionText), `${directionText} ${title(textAt(months, month - 1))}`));
|
|
246
|
+
}
|
|
191
247
|
const relativeMonth = String$1.match(/^([a-z]+\.?) (?:of )?(last|this|next) year$/u)(input);
|
|
192
248
|
if (Option.isSome(relativeMonth)) {
|
|
193
249
|
const directionText = textAt(relativeMonth.value, 2);
|
|
@@ -197,20 +253,15 @@ const parseBasePeriod = (input) => {
|
|
|
197
253
|
}
|
|
198
254
|
const standaloneMonth = monthNumber(input);
|
|
199
255
|
if (standaloneMonth !== void 0) return Option.some(monthOfRelativeYear(standaloneMonth, 0, title(textAt(months, standaloneMonth - 1))));
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (input === "last weekend" || input === "previous weekend") return Option.some(relativeWeekend(-1, "last weekend"));
|
|
205
|
-
if (input === "next weekend" || input === "coming weekend") return Option.some(relativeWeekend(1, "next weekend"));
|
|
206
|
-
if (input === "the weekend before last") return Option.some(relativeWeekend(-2, "the weekend before last"));
|
|
207
|
-
if (input === "the weekend after next") return Option.some(relativeWeekend(2, "the weekend after next"));
|
|
208
|
-
const articlePeriod = String$1.match(/^the (day|week|month|quarter|year)$/u)(input);
|
|
256
|
+
return Option.none();
|
|
257
|
+
};
|
|
258
|
+
const parseRelativeUnit = (input) => {
|
|
259
|
+
const articlePeriod = String$1.match(/^the ([^ ]+)$/u)(input);
|
|
209
260
|
if (Option.isSome(articlePeriod)) {
|
|
210
261
|
const entry = units.find((unit) => unit[0] === textAt(articlePeriod.value, 1));
|
|
211
262
|
if (entry !== void 0) return Option.some(relativePeriod(entry[1], 0, currentPeriod(entry[0])));
|
|
212
263
|
}
|
|
213
|
-
const outerRelative = String$1.match(/^(?:the )?(
|
|
264
|
+
const outerRelative = String$1.match(/^(?:the )?([^ ]+) (before last|after next)$/u)(input);
|
|
214
265
|
if (Option.isSome(outerRelative)) {
|
|
215
266
|
const entry = units.find((unit) => unit[0] === textAt(outerRelative.value, 1));
|
|
216
267
|
if (entry !== void 0) {
|
|
@@ -230,22 +281,69 @@ const parseBasePeriod = (input) => {
|
|
|
230
281
|
}
|
|
231
282
|
return Option.none();
|
|
232
283
|
};
|
|
284
|
+
const parseBasePeriod = (input) => {
|
|
285
|
+
const knownPeriod = Option.firstSomeOf([parseDatedPeriod(input), parseRelativeMonth(input)]);
|
|
286
|
+
if (Option.isSome(knownPeriod)) return knownPeriod;
|
|
287
|
+
if (input === "today") return Option.some(relativePeriod("day", 0, "today"));
|
|
288
|
+
if (input === "yesterday") return Option.some(relativePeriod("day", -1, "yesterday"));
|
|
289
|
+
if (input === "tomorrow") return Option.some(relativePeriod("day", 1, "tomorrow"));
|
|
290
|
+
if (input === "weekend" || input === "the weekend" || input === "this weekend") return Option.some(relativeWeekend(0, "this weekend"));
|
|
291
|
+
if (input === "last weekend" || input === "previous weekend") return Option.some(relativeWeekend(-1, "last weekend"));
|
|
292
|
+
if (input === "next weekend" || input === "coming weekend") return Option.some(relativeWeekend(1, "next weekend"));
|
|
293
|
+
if (input === "the weekend before last") return Option.some(relativeWeekend(-2, "the weekend before last"));
|
|
294
|
+
if (input === "the weekend after next") return Option.some(relativeWeekend(2, "the weekend after next"));
|
|
295
|
+
const weekday = nextWeekdayPhrases.indexOf(input);
|
|
296
|
+
if (weekday !== -1) return Option.some(relativeWeekday(weekday, 1, `next ${title(textAt(weekdays, weekday))}`));
|
|
297
|
+
return parseRelativeUnit(input);
|
|
298
|
+
};
|
|
299
|
+
const parsePeriodEdge = (input) => {
|
|
300
|
+
const edge = String$1.match(/^(?:the )?(start|beginning|end) of (.+)$/u)(input);
|
|
301
|
+
if (Option.isNone(edge)) return Option.none();
|
|
302
|
+
const periodText = textAt(edge.value, 2);
|
|
303
|
+
const basePeriod = parseBasePeriod(periodText);
|
|
304
|
+
const implicit = units.find((entry) => entry[0] === periodText);
|
|
305
|
+
const period = Option.isSome(basePeriod) || implicit === void 0 ? basePeriod : Option.some(relativePeriod(implicit[1], 0, currentPeriod(implicit[0])));
|
|
306
|
+
if (Option.isNone(period)) return Option.none();
|
|
307
|
+
const edgeName = textAt(edge.value, 1);
|
|
308
|
+
const name = edgeName === "beginning" ? "start" : edgeName;
|
|
309
|
+
const canonical = `${name} of ${period.value.canonical}`;
|
|
310
|
+
return Option.some(name === "end" ? periodEndDay(period.value, canonical) : periodStartDay(period.value, canonical));
|
|
311
|
+
};
|
|
233
312
|
const parsePeriod = (input) => {
|
|
313
|
+
const shifted = String$1.match(/^(.+) ([1-9]\d*) ([^ ]+) (ago|prior|from now)$/u)(input);
|
|
314
|
+
if (Option.isSome(shifted)) {
|
|
315
|
+
const amount = parseTrailingCount(textAt(shifted.value, 2));
|
|
316
|
+
const unitText = textAt(shifted.value, 3);
|
|
317
|
+
const period = parsePeriod(textAt(shifted.value, 1));
|
|
318
|
+
if (Option.isSome(amount) && Option.isSome(period)) {
|
|
319
|
+
const entry = countedUnit(unitText, amount.value);
|
|
320
|
+
if (entry !== void 0) {
|
|
321
|
+
const past = textAt(shifted.value, 4) !== "from now";
|
|
322
|
+
const direction = past ? -amount.value : amount.value;
|
|
323
|
+
const suffix = past ? "ago" : "from now";
|
|
324
|
+
const canonical = `${period.value.canonical} ${amount.value} ${unitText} ${suffix}`;
|
|
325
|
+
return Option.some(shiftPeriod(period.value, direction, entry[1], canonical));
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const relativeShift = String$1.match(/^([1-9]\d*) ([^ ]+) (before|after) (.+)$/u)(input);
|
|
330
|
+
if (Option.isSome(relativeShift)) {
|
|
331
|
+
const amount = parseTrailingCount(textAt(relativeShift.value, 1));
|
|
332
|
+
const unitText = textAt(relativeShift.value, 2);
|
|
333
|
+
const period = parsePeriod(textAt(relativeShift.value, 4));
|
|
334
|
+
if (Option.isSome(amount) && Option.isSome(period)) {
|
|
335
|
+
const entry = countedUnit(unitText, amount.value);
|
|
336
|
+
if (entry !== void 0) {
|
|
337
|
+
const direction = textAt(relativeShift.value, 3) === "before" ? -amount.value : amount.value;
|
|
338
|
+
return Option.some(shiftPeriod(period.value, direction, entry[1], input));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
234
342
|
const previousDay = String$1.match(/^(?:the )?day before (.+)$/u)(input);
|
|
235
343
|
if (Option.isSome(previousDay)) {
|
|
236
344
|
const period = parseBasePeriod(textAt(previousDay.value, 1));
|
|
237
345
|
if (Option.isSome(period)) return Option.some(periodPreviousDay(period.value, `the day before ${period.value.canonical}`));
|
|
238
346
|
}
|
|
239
|
-
const edge = String$1.match(/^(?:the )?(start|beginning|end) of (.+)$/u)(input);
|
|
240
|
-
if (Option.isSome(edge)) {
|
|
241
|
-
const period = parseBasePeriod(textAt(edge.value, 2));
|
|
242
|
-
if (Option.isSome(period)) {
|
|
243
|
-
const edgeName = textAt(edge.value, 1);
|
|
244
|
-
const name = edgeName === "beginning" ? "start" : edgeName;
|
|
245
|
-
const canonical = `${name} of ${period.value.canonical}`;
|
|
246
|
-
return Option.some(name === "end" ? periodEndDay(period.value, canonical) : periodStartDay(period.value, canonical));
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
347
|
const wrapper = [
|
|
250
348
|
"during ",
|
|
251
349
|
"in ",
|
|
@@ -253,7 +351,11 @@ const parsePeriod = (input) => {
|
|
|
253
351
|
"all of ",
|
|
254
352
|
"the whole of "
|
|
255
353
|
].find((prefix) => input.startsWith(prefix));
|
|
256
|
-
return
|
|
354
|
+
return Option.firstSomeOf([
|
|
355
|
+
parsePeriodEdge(input),
|
|
356
|
+
parseBasePeriod(wrapper === void 0 ? input : input.slice(wrapper.length)),
|
|
357
|
+
parseCalendarOffset(input)
|
|
358
|
+
]);
|
|
257
359
|
};
|
|
258
360
|
const boundaryCandidate = (input) => openBoundaryCandidate(input, [
|
|
259
361
|
["up to and including ", "through"],
|
|
@@ -287,7 +389,7 @@ const boundaryCandidate = (input) => openBoundaryCandidate(input, [
|
|
|
287
389
|
], parsePeriod);
|
|
288
390
|
const countedUnit = (value, amount) => units.find((entry) => value === (amount === 1 ? entry[0] : entry[2]));
|
|
289
391
|
const parseCalendarOffset = (input) => {
|
|
290
|
-
const singular = String$1.match(/^(?:(?:a|one) (
|
|
392
|
+
const singular = String$1.match(/^(?:(?:a|one) ([^ ]+) (ago|prior|from now)|in (?:a|one) ([^ ]+))$/u)(input);
|
|
291
393
|
if (Option.isSome(singular)) {
|
|
292
394
|
const unitText = textAt(singular.value, 1) || textAt(singular.value, 3);
|
|
293
395
|
const entry = units.find((unit) => unit[0] === unitText);
|
|
@@ -296,12 +398,12 @@ const parseCalendarOffset = (input) => {
|
|
|
296
398
|
const isPast = directionText === "ago" || directionText === "prior";
|
|
297
399
|
const direction = isPast ? -1 : 1;
|
|
298
400
|
const canonical = isPast ? `1 ${entry[0]} ago` : `in 1 ${entry[0]}`;
|
|
299
|
-
return Option.some(
|
|
401
|
+
return Option.some(relativePeriod(entry[1], direction, canonical));
|
|
300
402
|
}
|
|
301
403
|
}
|
|
302
|
-
const past = String$1.match(/^([1-9]\d*) (
|
|
303
|
-
const futureIn = String$1.match(/^in ([1-9]\d*) (
|
|
304
|
-
const futureFromNow = String$1.match(/^([1-9]\d*) (
|
|
404
|
+
const past = String$1.match(/^([1-9]\d*) ([^ ]+) (?:ago|prior)$/u)(input);
|
|
405
|
+
const futureIn = String$1.match(/^in ([1-9]\d*) ([^ ]+)$/u)(input);
|
|
406
|
+
const futureFromNow = String$1.match(/^([1-9]\d*) ([^ ]+) from now$/u)(input);
|
|
305
407
|
const match = Option.firstSomeOf([
|
|
306
408
|
past,
|
|
307
409
|
futureIn,
|
|
@@ -315,13 +417,13 @@ const parseCalendarOffset = (input) => {
|
|
|
315
417
|
if (entry === void 0) return Option.none();
|
|
316
418
|
const direction = Option.isSome(past) ? -amount.value : amount.value;
|
|
317
419
|
const canonical = direction < 0 ? `${amount.value} ${unitText} ago` : `in ${amount.value} ${unitText}`;
|
|
318
|
-
return Option.some(
|
|
420
|
+
return Option.some(relativePeriod(entry[1], direction, canonical));
|
|
319
421
|
};
|
|
320
422
|
const parseRollingPeriod = (input) => {
|
|
321
|
-
const past = String$1.match(/^(?:(?:last|previous|past) |(?:in|over) the (?:last|previous|past) )?([1-9]\d*) (
|
|
322
|
-
const singularPast = String$1.match(/^(?:past|the past|in the past|over the past|this past) (
|
|
323
|
-
const future = String$1.match(/^(?:(?:next|coming|within) |(?:in|over|within) the (?:next|coming) )([1-9]\d*) (
|
|
324
|
-
const singularFuture = String$1.match(/^(?:in|over|within) the (?:next|coming) (
|
|
423
|
+
const past = String$1.match(/^(?:(?:last|previous|past) |(?:in|over) the (?:last|previous|past) )?([1-9]\d*) ([^ ]+)$/u)(input);
|
|
424
|
+
const singularPast = String$1.match(/^(?:past|the past|in the past|over the past|this past) ([^ ]+)$/u)(input);
|
|
425
|
+
const future = String$1.match(/^(?:(?:next|coming|within) |(?:in|over|within) the (?:next|coming) )([1-9]\d*) ([^ ]+)$/u)(input);
|
|
426
|
+
const singularFuture = String$1.match(/^(?:in|over|within) the (?:next|coming) ([^ ]+)$/u)(input);
|
|
325
427
|
const match = Option.firstSomeOf([
|
|
326
428
|
past,
|
|
327
429
|
singularPast,
|
|
@@ -371,8 +473,6 @@ const parseEnglish = (input) => {
|
|
|
371
473
|
"from now on",
|
|
372
474
|
"starting now"
|
|
373
475
|
].includes(input)) return Option.some(candidate(fromNowRange(), "from now"));
|
|
374
|
-
const offset = parseCalendarOffset(input);
|
|
375
|
-
if (Option.isSome(offset)) return offset;
|
|
376
476
|
const rolling = parseRollingPeriod(input);
|
|
377
477
|
if (Option.isSome(rolling)) return rolling;
|
|
378
478
|
const toDate = toDatePhrases.find((entry) => entry.phrase === input);
|
|
@@ -389,7 +489,8 @@ const parseEnglish = (input) => {
|
|
|
389
489
|
"from now to ",
|
|
390
490
|
"from now until ",
|
|
391
491
|
"from now through ",
|
|
392
|
-
"between now and "
|
|
492
|
+
"between now and ",
|
|
493
|
+
"now to "
|
|
393
494
|
], parsePeriod, (period) => `from ${period} to now`, (period) => `from now to ${period}`);
|
|
394
495
|
if (Option.isSome(nowBounded)) return nowBounded;
|
|
395
496
|
const bounded = joinedPeriodCandidate(input, [
|
|
@@ -457,9 +558,12 @@ const staticPeriodPhrases = [
|
|
|
457
558
|
]),
|
|
458
559
|
...months.flatMap((month) => [
|
|
459
560
|
month,
|
|
561
|
+
`last ${month}`,
|
|
562
|
+
`next ${month}`,
|
|
460
563
|
`${month} of last year`,
|
|
461
564
|
`${month} of next year`
|
|
462
|
-
])
|
|
565
|
+
]),
|
|
566
|
+
...nextWeekdayPhrases
|
|
463
567
|
];
|
|
464
568
|
const staticPeriods = periodsFromPhrases(staticPeriodPhrases, parsePeriod);
|
|
465
569
|
const boundaryPrefixes = [
|
|
@@ -501,6 +605,17 @@ const suggestEnglish = (input, limit) => {
|
|
|
501
605
|
], limit);
|
|
502
606
|
};
|
|
503
607
|
const renderEnglish = (range) => {
|
|
608
|
+
const shifted = decomposeShiftedPeriodRange(range);
|
|
609
|
+
if (Option.isSome(shifted)) {
|
|
610
|
+
const base = renderEnglish(shifted.value.baseRange);
|
|
611
|
+
const entry = units.find((unit) => unit[1] === shifted.value.unit);
|
|
612
|
+
if (Option.isSome(base) && entry !== void 0) {
|
|
613
|
+
const amount = Math.abs(shifted.value.amount);
|
|
614
|
+
const noun = amount === 1 ? entry[0] : entry[2];
|
|
615
|
+
const suffix = shifted.value.amount < 0 ? "ago" : "from now";
|
|
616
|
+
return Option.some(`${base.value} ${amount} ${noun} ${suffix}`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
504
619
|
const offset = calendarPeriodOffset(range);
|
|
505
620
|
if (Option.isSome(offset) && Math.abs(offset.value.amount) > 1) {
|
|
506
621
|
const entry = units.find((unit) => unit[1] === offset.value.unit);
|
|
@@ -524,12 +639,13 @@ const renderEnglish = (range) => {
|
|
|
524
639
|
...currentYearDatePeriods(range, currentDateLabel),
|
|
525
640
|
...periodsFromPhrases([...datedPeriods(range, months), ...datedQuarterPeriods(range)], parsePeriod)
|
|
526
641
|
];
|
|
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");
|
|
642
|
+
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, ...periods.map((period) => periodPreviousDay(period, `the day before ${period.canonical}`))], (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");
|
|
528
643
|
};
|
|
529
644
|
const EnglishContribution = new BaseLanguageContribution({
|
|
530
645
|
locale: "en",
|
|
531
646
|
vocabulary: [
|
|
532
647
|
...months,
|
|
648
|
+
...weekdays,
|
|
533
649
|
...quarterNames,
|
|
534
650
|
"q1",
|
|
535
651
|
"q2",
|
|
@@ -583,8 +699,8 @@ const EnglishContribution = new BaseLanguageContribution({
|
|
|
583
699
|
"weekend",
|
|
584
700
|
"yesterday"
|
|
585
701
|
],
|
|
586
|
-
normalize:
|
|
587
|
-
correct:
|
|
702
|
+
normalize: normalizeEnglish,
|
|
703
|
+
correct: correctEnglish,
|
|
588
704
|
parseExact: parseEnglish,
|
|
589
705
|
suggest: suggestEnglish,
|
|
590
706
|
render: renderEnglish
|