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/fr.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, periodDay, 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/fr.ts
|
|
10
10
|
const months = [
|
|
@@ -50,6 +50,9 @@ const units = [
|
|
|
50
50
|
unit: "day",
|
|
51
51
|
singular: "jour",
|
|
52
52
|
plural: "jours",
|
|
53
|
+
rollingSingular: "jour",
|
|
54
|
+
rollingPlural: "jours",
|
|
55
|
+
rollingGender: "masculine",
|
|
53
56
|
current: "aujourd'hui",
|
|
54
57
|
previous: "hier",
|
|
55
58
|
next: "demain",
|
|
@@ -60,6 +63,9 @@ const units = [
|
|
|
60
63
|
unit: "week",
|
|
61
64
|
singular: "semaine",
|
|
62
65
|
plural: "semaines",
|
|
66
|
+
rollingSingular: "semaine",
|
|
67
|
+
rollingPlural: "semaines",
|
|
68
|
+
rollingGender: "feminine",
|
|
63
69
|
current: "cette semaine",
|
|
64
70
|
previous: "la semaine dernière",
|
|
65
71
|
next: "la semaine prochaine",
|
|
@@ -70,6 +76,9 @@ const units = [
|
|
|
70
76
|
unit: "month",
|
|
71
77
|
singular: "mois",
|
|
72
78
|
plural: "mois",
|
|
79
|
+
rollingSingular: "mois",
|
|
80
|
+
rollingPlural: "mois",
|
|
81
|
+
rollingGender: "masculine",
|
|
73
82
|
current: "ce mois-ci",
|
|
74
83
|
previous: "le mois dernier",
|
|
75
84
|
next: "le mois prochain",
|
|
@@ -80,6 +89,9 @@ const units = [
|
|
|
80
89
|
unit: "quarter",
|
|
81
90
|
singular: "trimestre",
|
|
82
91
|
plural: "trimestres",
|
|
92
|
+
rollingSingular: "trimestre",
|
|
93
|
+
rollingPlural: "trimestres",
|
|
94
|
+
rollingGender: "masculine",
|
|
83
95
|
current: "ce trimestre",
|
|
84
96
|
previous: "le trimestre dernier",
|
|
85
97
|
next: "le trimestre prochain",
|
|
@@ -90,6 +102,9 @@ const units = [
|
|
|
90
102
|
unit: "year",
|
|
91
103
|
singular: "an",
|
|
92
104
|
plural: "ans",
|
|
105
|
+
rollingSingular: "année",
|
|
106
|
+
rollingPlural: "années",
|
|
107
|
+
rollingGender: "feminine",
|
|
93
108
|
current: "cette année",
|
|
94
109
|
previous: "l'année dernière",
|
|
95
110
|
next: "l'année prochaine",
|
|
@@ -97,7 +112,41 @@ const units = [
|
|
|
97
112
|
remaining: "reste de l'année"
|
|
98
113
|
}
|
|
99
114
|
];
|
|
100
|
-
const
|
|
115
|
+
const rollingArticle = (entry) => entry.rollingGender === "feminine" ? "la" : "le";
|
|
116
|
+
const indefiniteArticle = (entry) => entry.unit === "week" ? "une" : "un";
|
|
117
|
+
const rollingModifier = (entry, future, plural) => {
|
|
118
|
+
if (future) {
|
|
119
|
+
if (entry.rollingGender === "feminine") return plural ? "prochaines" : "prochaine";
|
|
120
|
+
return plural ? "prochains" : "prochain";
|
|
121
|
+
}
|
|
122
|
+
if (entry.rollingGender === "feminine") return plural ? "dernières" : "dernière";
|
|
123
|
+
return plural ? "derniers" : "dernier";
|
|
124
|
+
};
|
|
125
|
+
const withDe = (period) => {
|
|
126
|
+
if (period.startsWith("le ")) return `du ${period.slice(3)}`;
|
|
127
|
+
if (period.startsWith("les ")) return `des ${period.slice(4)}`;
|
|
128
|
+
if (period.startsWith("la ")) return `de la ${period.slice(3)}`;
|
|
129
|
+
if (period.startsWith("l'")) return `de ${period}`;
|
|
130
|
+
return `de ${period}`;
|
|
131
|
+
};
|
|
132
|
+
const withA = (period) => {
|
|
133
|
+
if (period.startsWith("le ")) return `au ${period.slice(3)}`;
|
|
134
|
+
if (period.startsWith("les ")) return `aux ${period.slice(4)}`;
|
|
135
|
+
if (period.startsWith("la ")) return `à la ${period.slice(3)}`;
|
|
136
|
+
if (period.startsWith("l'")) return `à ${period}`;
|
|
137
|
+
return `à ${period}`;
|
|
138
|
+
};
|
|
139
|
+
const afterDe = (period) => {
|
|
140
|
+
if (period.startsWith("du ")) return `le ${period.slice(3)}`;
|
|
141
|
+
if (period.startsWith("de la ")) return `la ${period.slice(6)}`;
|
|
142
|
+
if (period.startsWith("de l'")) return `l'${period.slice(5)}`;
|
|
143
|
+
return period.startsWith("de ") ? period.slice(3) : period;
|
|
144
|
+
};
|
|
145
|
+
const startsWithDay = (period) => period[0] !== void 0 && period[0] >= "0" && period[0] <= "9";
|
|
146
|
+
const rangeLabel = (lower, upper) => {
|
|
147
|
+
return `${startsWithDay(lower) ? `du ${lower}` : withDe(lower)} ${startsWithDay(upper) ? `au ${upper}` : withA(upper)}`;
|
|
148
|
+
};
|
|
149
|
+
const untilLabel = (period) => `jusqu'${withA(period)}`;
|
|
101
150
|
const unitAliases = [
|
|
102
151
|
["jour", "day"],
|
|
103
152
|
["jours", "day"],
|
|
@@ -362,17 +411,30 @@ const monthNumber = (value) => {
|
|
|
362
411
|
return short === -1 ? void 0 : short + 1;
|
|
363
412
|
};
|
|
364
413
|
const dateLabel = (day, month, year) => `${day === 1 ? "1er" : day} ${textAt(months, month - 1)} ${year}`;
|
|
414
|
+
const currentDateLabel = (day, month) => `${day === 1 ? "1er" : day} ${textAt(months, month - 1)}`;
|
|
365
415
|
const parseNamedDate = (input) => {
|
|
366
|
-
const named = String$1.match(/^(?:le )?(1er|[0-3]?\d)(?: de)? ([a-zàâçéèêëîïôûùüÿœ]+\.?)(?: de)? (\d{4})$/u)(input);
|
|
416
|
+
const named = String$1.match(/^(?:le )?(1er|[0-3]?\d)(?: de)? ([a-zàâçéèêëîïôûùüÿœ]+\.?),?(?: de)? (\d{4})$/u)(input);
|
|
367
417
|
if (Option.isSome(named)) return namedDatePeriod(textAt(named.value, 3), textAt(named.value, 2), textAt(named.value, 1).replace(/er$/u, ""), monthNumber, dateLabel);
|
|
368
418
|
const numeric = String$1.match(/^([0-3]?\d)[./-]([01]?\d)[./-](\d{4})$/u)(input);
|
|
369
|
-
if (Option.
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
419
|
+
if (Option.isSome(numeric)) {
|
|
420
|
+
const year = validYear(textAt(numeric.value, 3));
|
|
421
|
+
const month = Number(textAt(numeric.value, 2));
|
|
422
|
+
const day = Number(textAt(numeric.value, 1));
|
|
423
|
+
if (year !== void 0 && month >= 1 && month <= 12) {
|
|
424
|
+
const value = isoDate(year, month, day);
|
|
425
|
+
if (isIsoDate(value) && value !== "9999-12-31") return Option.some(fixedDatePeriod(value, dateLabel(day, month, year)));
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
const current = String$1.match(/^(?:le )?(1er|[0-3]?\d)(?: de)? ([a-zàâçéèêëîïôûùüÿœ]+\.?)$/u)(input);
|
|
429
|
+
if (Option.isSome(current)) return namedCurrentYearDatePeriod(textAt(current.value, 2), textAt(current.value, 1).replace(/er$/u, ""), monthNumber, currentDateLabel);
|
|
430
|
+
const relative = String$1.match(/^(?:le )?(1er|[0-3]?\d) (de .+|du .+)$/u)(input);
|
|
431
|
+
if (Option.isNone(relative)) return Option.none();
|
|
432
|
+
const periodText = afterDe(textAt(relative.value, 2));
|
|
433
|
+
const alias = periodAliases.find((entry) => entry[0] === periodText && entry[1] === "month");
|
|
434
|
+
if (alias === void 0) return Option.none();
|
|
435
|
+
const day = Number(textAt(relative.value, 1).replace(/er$/u, ""));
|
|
436
|
+
const month = relativePeriod(alias[1], alias[2], alias[3]);
|
|
437
|
+
return periodDay(month, day, `${day === 1 ? "1er" : day} ${withDe(alias[3])}`);
|
|
376
438
|
};
|
|
377
439
|
const quarterNumber = (value) => {
|
|
378
440
|
if ((value.startsWith("q") || value.startsWith("t")) && value.length === 2) return Number(value.slice(1));
|
|
@@ -400,7 +462,8 @@ const parseQuarter = (input) => {
|
|
|
400
462
|
return quarter === void 0 ? Option.none() : Option.some(quarterOfRelativeYear(quarter, 0, `T${quarter}`));
|
|
401
463
|
};
|
|
402
464
|
const parseBasePeriod = (input) => {
|
|
403
|
-
|
|
465
|
+
const absoluteDate = absoluteDatePeriod(input, "fr");
|
|
466
|
+
if (Option.isSome(absoluteDate)) return absoluteDate;
|
|
404
467
|
const namedDate = parseNamedDate(input);
|
|
405
468
|
if (Option.isSome(namedDate)) return namedDate;
|
|
406
469
|
const quarter = parseQuarter(input);
|
|
@@ -414,17 +477,17 @@ const parseBasePeriod = (input) => {
|
|
|
414
477
|
if (Option.isSome(monthYear)) {
|
|
415
478
|
const month = monthNumber(textAt(monthYear.value, 1));
|
|
416
479
|
const year = validYear(textAt(monthYear.value, 2));
|
|
417
|
-
if (month !== void 0 && year !== void 0) return Option.some(fixedMonthPeriod(year, month, `${
|
|
480
|
+
if (month !== void 0 && year !== void 0) return Option.some(fixedMonthPeriod(year, month, `${textAt(months, month - 1)} ${year}`));
|
|
418
481
|
}
|
|
419
482
|
const relativeMonth = String$1.match(/^([a-zàâçéèêëîïôûùüÿœ]+\.?) de (l'année dernière|l'annee derniere|l'année prochaine|l'annee prochaine|cette année|cette annee)$/u)(input);
|
|
420
483
|
if (Option.isSome(relativeMonth)) {
|
|
421
484
|
const month = monthNumber(textAt(relativeMonth.value, 1));
|
|
422
485
|
const yearText = textAt(relativeMonth.value, 2);
|
|
423
486
|
const direction = relativeYearDirection(yearText);
|
|
424
|
-
if (month !== void 0) return Option.some(monthOfRelativeYear(month, direction, `${
|
|
487
|
+
if (month !== void 0) return Option.some(monthOfRelativeYear(month, direction, `${textAt(months, month - 1)} de ${relativeYearName(direction)}`));
|
|
425
488
|
}
|
|
426
489
|
const standaloneMonth = monthNumber(input);
|
|
427
|
-
if (standaloneMonth !== void 0) return Option.some(monthOfRelativeYear(standaloneMonth, 0,
|
|
490
|
+
if (standaloneMonth !== void 0) return Option.some(monthOfRelativeYear(standaloneMonth, 0, textAt(months, standaloneMonth - 1)));
|
|
428
491
|
const alias = periodAliases.find((entry) => entry[0] === input);
|
|
429
492
|
if (alias !== void 0) return Option.some(relativePeriod(alias[1], alias[2], alias[3]));
|
|
430
493
|
if ([
|
|
@@ -455,7 +518,7 @@ const parsePeriod = (input) => {
|
|
|
455
518
|
const period = parseBasePeriod(textAt(edge.value, 2));
|
|
456
519
|
if (Option.isSome(period)) {
|
|
457
520
|
const isEnd = edgeName === "fin";
|
|
458
|
-
const canonical = `${isEnd ? "fin" : "début"}
|
|
521
|
+
const canonical = `${isEnd ? "fin" : "début"} ${withDe(period.value.canonical)}`;
|
|
459
522
|
return Option.some(isEnd ? periodEndDay(period.value, canonical) : periodStartDay(period.value, canonical));
|
|
460
523
|
}
|
|
461
524
|
}
|
|
@@ -468,7 +531,27 @@ const parsePeriod = (input) => {
|
|
|
468
531
|
].find((prefix) => input.startsWith(prefix));
|
|
469
532
|
return parseBasePeriod(wrapper === void 0 ? input : input.slice(wrapper.length));
|
|
470
533
|
};
|
|
471
|
-
const countedUnit = (value
|
|
534
|
+
const countedUnit = (value, amount) => {
|
|
535
|
+
const plural = value === "mois" || value.endsWith("s");
|
|
536
|
+
if (value !== "mois" && plural === (amount === 1)) return void 0;
|
|
537
|
+
const unit = unitAliases.find((entry) => entry[0] === value)?.[1];
|
|
538
|
+
return unit === void 0 ? void 0 : units.find((entry) => entry.unit === unit);
|
|
539
|
+
};
|
|
540
|
+
const modifierAgreesWithNoun = (input, noun) => {
|
|
541
|
+
const match = String$1.match(/(?:^| )(derniers|dernières|dernieres|passés|passées|passes|précédents|précédentes|precedents|precedentes|prochains|prochaines|suivants|suivantes)(?: |$)/u)(input);
|
|
542
|
+
if (Option.isNone(match)) return true;
|
|
543
|
+
const modifier = textAt(match.value, 1);
|
|
544
|
+
return (noun.startsWith("semaine") || noun.startsWith("année") || noun.startsWith("annee")) === [
|
|
545
|
+
"dernières",
|
|
546
|
+
"dernieres",
|
|
547
|
+
"passées",
|
|
548
|
+
"passes",
|
|
549
|
+
"précédentes",
|
|
550
|
+
"precedentes",
|
|
551
|
+
"prochaines",
|
|
552
|
+
"suivantes"
|
|
553
|
+
].includes(modifier);
|
|
554
|
+
};
|
|
472
555
|
const countedUnitPattern = "jour|jours|semaine|semaines|mois|trimestre|trimestres|an|ans|année|annee|années|annees";
|
|
473
556
|
const compileCountedPattern = (source) => new RegExp(source.replace("UNIT", countedUnitPattern), "u");
|
|
474
557
|
const calendarPastPattern = compileCountedPattern("^il y a ([1-9]\\d*) (UNIT)$");
|
|
@@ -477,38 +560,89 @@ const rollingSincePattern = compileCountedPattern("^depuis ([1-9]\\d*) (UNIT)$")
|
|
|
477
560
|
const rollingBarePattern = compileCountedPattern("^([1-9]\\d*) (UNIT)$");
|
|
478
561
|
const rollingPastPatterns = [
|
|
479
562
|
compileCountedPattern("^(?:(?:les|la) )?(?:derniers|dernières|dernieres|passés|passées|passes|précédents|précédentes|precedents|precedentes) ([1-9]\\d*) (UNIT)$"),
|
|
480
|
-
compileCountedPattern("^(?:(?:les|la) )?([1-9]\\d*) (?:derniers|dernières|dernieres|passés|passées|passes|précédents|précédentes|precedents|precedentes) (UNIT)$"),
|
|
563
|
+
compileCountedPattern("^(?:(?:les|la|au cours des|pendant les) )?([1-9]\\d*) (?:derniers|dernières|dernieres|passés|passées|passes|précédents|précédentes|precedents|precedentes) (UNIT)$"),
|
|
481
564
|
compileCountedPattern("^([1-9]\\d*) (UNIT) (?:derniers|dernières|dernieres|passés|passées|passes|précédents|précédentes|precedents|precedentes)$")
|
|
482
565
|
];
|
|
483
566
|
const rollingFuturePatterns = [
|
|
484
567
|
compileCountedPattern("^(?:(?:les|la) )?(?:prochains|prochaines|suivants|suivantes) ([1-9]\\d*) (UNIT)$"),
|
|
485
|
-
compileCountedPattern("^(?:(?:les|la) )?([1-9]\\d*) (?:prochains|prochaines|suivants|suivantes) (UNIT)$"),
|
|
568
|
+
compileCountedPattern("^(?:(?:les|la|au cours des|pendant les) )?([1-9]\\d*) (?:prochains|prochaines|suivants|suivantes) (UNIT)$"),
|
|
486
569
|
compileCountedPattern("^([1-9]\\d*) (UNIT) (?:prochains|prochaines|suivants|suivantes|à venir)$")
|
|
487
570
|
];
|
|
488
571
|
const firstPatternMatch = (input, patterns) => Option.firstSomeOf(patterns.map((pattern) => String$1.match(pattern)(input)));
|
|
489
|
-
const isFeminineUnit = (unit) => unit === "week";
|
|
490
572
|
const relativeCountPhrase = (amount, entry, future) => {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
if (isFeminineUnit(entry.unit)) modifier = future ? "prochaines" : "dernières";
|
|
494
|
-
return `les ${amount} ${modifier} ${noun}`;
|
|
573
|
+
if (amount === 1) return singularRollingCanonical(entry, future);
|
|
574
|
+
return `les ${amount} ${rollingModifier(entry, future, true)} ${entry.rollingPlural}`;
|
|
495
575
|
};
|
|
576
|
+
const singularRollingCanonical = (entry, future) => {
|
|
577
|
+
const article = indefiniteArticle(entry);
|
|
578
|
+
return future ? `à partir de maintenant pendant ${article} ${entry.singular}` : `depuis ${article} ${entry.singular}`;
|
|
579
|
+
};
|
|
580
|
+
const singularRollingPhrases = units.flatMap((entry) => {
|
|
581
|
+
const period = `${rollingArticle(entry)} ${rollingModifier(entry, false, false)} ${entry.rollingSingular}`;
|
|
582
|
+
const futurePeriod = `${rollingArticle(entry)} ${rollingModifier(entry, true, false)} ${entry.rollingSingular}`;
|
|
583
|
+
const article = indefiniteArticle(entry);
|
|
584
|
+
return [
|
|
585
|
+
{
|
|
586
|
+
phrase: period,
|
|
587
|
+
entry,
|
|
588
|
+
future: false
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
phrase: `au cours ${withDe(period)}`,
|
|
592
|
+
entry,
|
|
593
|
+
future: false
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
phrase: `depuis ${article} ${entry.singular}`,
|
|
597
|
+
entry,
|
|
598
|
+
future: false
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
phrase: `pendant ${futurePeriod}`,
|
|
602
|
+
entry,
|
|
603
|
+
future: true
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
phrase: `à partir de maintenant pendant ${article} ${entry.singular}`,
|
|
607
|
+
entry,
|
|
608
|
+
future: true
|
|
609
|
+
}
|
|
610
|
+
];
|
|
611
|
+
});
|
|
612
|
+
const singularCalendarOffsets = units.flatMap((entry) => {
|
|
613
|
+
const quantity = `${indefiniteArticle(entry)} ${entry.singular}`;
|
|
614
|
+
return [{
|
|
615
|
+
phrase: `il y a ${quantity}`,
|
|
616
|
+
entry,
|
|
617
|
+
direction: -1
|
|
618
|
+
}, {
|
|
619
|
+
phrase: `dans ${quantity}`,
|
|
620
|
+
entry,
|
|
621
|
+
direction: 1
|
|
622
|
+
}];
|
|
623
|
+
});
|
|
496
624
|
const parseCalendarOffset = (input) => {
|
|
625
|
+
const singular = singularCalendarOffsets.find((entry) => entry.phrase === input);
|
|
626
|
+
if (singular !== void 0) return Option.some(candidate(periodRange(relativePeriod(singular.entry.unit, singular.direction, singular.phrase)), singular.phrase));
|
|
497
627
|
const past = String$1.match(calendarPastPattern)(input);
|
|
498
628
|
const future = String$1.match(calendarFuturePattern)(input);
|
|
499
629
|
const match = Option.firstSomeOf([past, future]);
|
|
500
630
|
if (Option.isNone(match)) return Option.none();
|
|
501
631
|
const amount = parseTrailingCount(textAt(match.value, 1));
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
const entry = units.find((item) => item.unit === unit);
|
|
632
|
+
if (Option.isNone(amount)) return Option.none();
|
|
633
|
+
const entry = countedUnit(textAt(match.value, 2), amount.value);
|
|
505
634
|
if (entry === void 0) return Option.none();
|
|
506
635
|
const direction = Option.isSome(past) ? -amount.value : amount.value;
|
|
507
636
|
const noun = amount.value === 1 ? entry.singular : entry.plural;
|
|
508
637
|
const canonical = direction < 0 ? `il y a ${amount.value} ${noun}` : `dans ${amount.value} ${noun}`;
|
|
509
|
-
return Option.some(candidate(periodRange(relativePeriod(unit, direction, canonical)), canonical));
|
|
638
|
+
return Option.some(candidate(periodRange(relativePeriod(entry.unit, direction, canonical)), canonical));
|
|
510
639
|
};
|
|
511
640
|
const parseRollingPeriod = (input) => {
|
|
641
|
+
const singular = singularRollingPhrases.find((entry) => entry.phrase === input);
|
|
642
|
+
if (singular !== void 0) {
|
|
643
|
+
const range = singular.future ? futureRange(1, singular.entry.unit) : trailingRange(1, singular.entry.unit);
|
|
644
|
+
return Option.some(candidate(range, singularRollingCanonical(singular.entry, singular.future)));
|
|
645
|
+
}
|
|
512
646
|
const since = String$1.match(rollingSincePattern)(input);
|
|
513
647
|
const past = firstPatternMatch(input, rollingPastPatterns);
|
|
514
648
|
const bare = String$1.match(rollingBarePattern)(input);
|
|
@@ -521,19 +655,31 @@ const parseRollingPeriod = (input) => {
|
|
|
521
655
|
]);
|
|
522
656
|
if (Option.isNone(match)) return Option.none();
|
|
523
657
|
const amount = parseTrailingCount(textAt(match.value, 1));
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
const entry =
|
|
527
|
-
if (entry === void 0) return Option.none();
|
|
658
|
+
if (Option.isNone(amount)) return Option.none();
|
|
659
|
+
const noun = textAt(match.value, 2);
|
|
660
|
+
const entry = countedUnit(noun, amount.value);
|
|
661
|
+
if (entry === void 0 || !modifierAgreesWithNoun(input, noun)) return Option.none();
|
|
528
662
|
const isFuture = Option.isSome(future);
|
|
529
|
-
const range = isFuture ? futureRange(amount.value, unit) : trailingRange(amount.value, unit);
|
|
663
|
+
const range = isFuture ? futureRange(amount.value, entry.unit) : trailingRange(amount.value, entry.unit);
|
|
530
664
|
return Option.some(candidate(range, relativeCountPhrase(amount.value, entry, isFuture)));
|
|
531
665
|
};
|
|
666
|
+
const parseElidedDateRange = (input) => {
|
|
667
|
+
const joined = String$1.match(/^(?:du (1er|[0-3]?\d) au|entre le (1er|[0-3]?\d) et le) (1er|[0-3]?\d) (.+)$/u)(input);
|
|
668
|
+
const dashed = String$1.match(/^(1er|[0-3]?\d)[–—-](1er|[0-3]?\d) (.+)$/u)(input);
|
|
669
|
+
const match = Option.firstSomeOf([joined, dashed]);
|
|
670
|
+
if (Option.isNone(match)) return Option.none();
|
|
671
|
+
const isJoined = Option.isSome(joined);
|
|
672
|
+
const lowerDay = textAt(match.value, 1) || textAt(match.value, 2);
|
|
673
|
+
const upperDay = textAt(match.value, isJoined ? 3 : 2);
|
|
674
|
+
const period = afterDe(textAt(match.value, isJoined ? 4 : 3));
|
|
675
|
+
const datePeriod = periodAliases.some((entry) => entry[0] === period && entry[1] === "month") ? withDe(period) : period;
|
|
676
|
+
return joinedPeriodCandidate(`du ${lowerDay} ${datePeriod} au ${upperDay} ${datePeriod}`, [["du ", " au "]], parsePeriod, rangeLabel);
|
|
677
|
+
};
|
|
532
678
|
const boundaryCandidate = (input) => {
|
|
533
679
|
const included = String$1.match(/^jusqu'à (.+) (?:inclus|incluse|inclusivement)$/u)(input);
|
|
534
680
|
if (Option.isSome(included)) {
|
|
535
681
|
const period = parsePeriod(textAt(included.value, 1));
|
|
536
|
-
if (Option.isSome(period)) return Option.some(periodBoundaryCandidate(period.value, "through",
|
|
682
|
+
if (Option.isSome(period)) return Option.some(periodBoundaryCandidate(period.value, "through", untilLabel(period.value.canonical)));
|
|
537
683
|
}
|
|
538
684
|
return openBoundaryCandidate(input, [
|
|
539
685
|
["jusqu'avant ", "before"],
|
|
@@ -568,6 +714,8 @@ const parseFrench = (input) => {
|
|
|
568
714
|
if (Option.isSome(rolling)) return rolling;
|
|
569
715
|
const toDate = toDatePhrases.find((entry) => entry.phrase === input);
|
|
570
716
|
if (toDate !== void 0) return Option.some(candidate(periodToDateRange(toDate.entry.unit), toDate.entry.toDate));
|
|
717
|
+
const elided = parseElidedDateRange(input);
|
|
718
|
+
if (Option.isSome(elided)) return elided;
|
|
571
719
|
const nowBounded = joinedNowCandidate(input, [
|
|
572
720
|
["depuis ", " jusqu'à maintenant"],
|
|
573
721
|
["depuis ", " jusqu'à aujourd'hui"],
|
|
@@ -588,20 +736,32 @@ const parseFrench = (input) => {
|
|
|
588
736
|
["", " — "],
|
|
589
737
|
["", " jusqu'à "],
|
|
590
738
|
["", " au "]
|
|
591
|
-
], parsePeriod,
|
|
739
|
+
], parsePeriod, rangeLabel);
|
|
592
740
|
if (Option.isSome(bounded)) return bounded;
|
|
593
741
|
const boundary = boundaryCandidate(input);
|
|
594
742
|
if (Option.isSome(boundary)) return boundary;
|
|
595
|
-
return Option.map(
|
|
743
|
+
return parsePeriod(input).pipe(Option.map((period) => candidate(periodRange(period), period.canonical)));
|
|
596
744
|
};
|
|
597
|
-
const
|
|
598
|
-
...periodAliases.map((entry) => entry[0]),
|
|
745
|
+
const weekendPhrases = [
|
|
599
746
|
"ce week-end",
|
|
600
747
|
"le week-end dernier",
|
|
601
748
|
"le week-end prochain",
|
|
602
749
|
"l'avant-dernier week-end",
|
|
603
|
-
"le week-end après le prochain"
|
|
604
|
-
|
|
750
|
+
"le week-end après le prochain"
|
|
751
|
+
];
|
|
752
|
+
const edgePeriodPhrases = [
|
|
753
|
+
...units.filter((entry) => entry.unit !== "day").flatMap((entry) => [
|
|
754
|
+
entry.current,
|
|
755
|
+
entry.previous,
|
|
756
|
+
entry.next
|
|
757
|
+
]),
|
|
758
|
+
...weekendPhrases,
|
|
759
|
+
...months
|
|
760
|
+
].flatMap((period) => [`début ${withDe(period)}`, `fin ${withDe(period)}`]);
|
|
761
|
+
const staticPeriodPhrases = [
|
|
762
|
+
...periodAliases.map((entry) => entry[0]),
|
|
763
|
+
...weekendPhrases,
|
|
764
|
+
...edgePeriodPhrases,
|
|
605
765
|
...[
|
|
606
766
|
1,
|
|
607
767
|
2,
|
|
@@ -631,15 +791,16 @@ const countedSuggestions = (input) => {
|
|
|
631
791
|
if (amount === void 0) return [];
|
|
632
792
|
return units.flatMap((entry) => {
|
|
633
793
|
const noun = amount === 1 ? entry.singular : entry.plural;
|
|
634
|
-
const
|
|
635
|
-
const
|
|
794
|
+
const rollingNoun = amount === 1 ? entry.rollingSingular : entry.rollingPlural;
|
|
795
|
+
const past = rollingModifier(entry, false, amount !== 1);
|
|
796
|
+
const future = rollingModifier(entry, true, amount !== 1);
|
|
636
797
|
return [
|
|
637
798
|
relativeCountPhrase(amount, entry, false),
|
|
638
|
-
`${amount} ${past} ${
|
|
799
|
+
`${amount} ${past} ${rollingNoun}`,
|
|
639
800
|
`${amount} ${noun}`,
|
|
640
801
|
relativeCountPhrase(amount, entry, true),
|
|
641
|
-
`${amount} ${future} ${
|
|
642
|
-
`${amount} ${
|
|
802
|
+
`${amount} ${future} ${rollingNoun}`,
|
|
803
|
+
`${amount} ${rollingNoun} à venir`,
|
|
643
804
|
`il y a ${amount} ${noun}`,
|
|
644
805
|
`dans ${amount} ${noun}`
|
|
645
806
|
];
|
|
@@ -648,6 +809,8 @@ const countedSuggestions = (input) => {
|
|
|
648
809
|
const frenchSuggestionPhrases = [
|
|
649
810
|
...units.map((entry) => entry.toDate),
|
|
650
811
|
...units.map((entry) => entry.remaining),
|
|
812
|
+
...singularRollingPhrases.map((entry) => entry.phrase),
|
|
813
|
+
...singularCalendarOffsets.map((entry) => entry.phrase),
|
|
651
814
|
...staticPeriodPhrases,
|
|
652
815
|
...prefixNaturalPhrases(staticPeriodPhrases, boundaryPrefixes),
|
|
653
816
|
"jusqu'à maintenant",
|
|
@@ -675,18 +838,19 @@ const renderFrench = (range) => {
|
|
|
675
838
|
const future = futurePeriod(range);
|
|
676
839
|
if (Option.isSome(future)) {
|
|
677
840
|
const entry = units.find((unit) => unit.unit === future.value.unit);
|
|
678
|
-
if (entry !== void 0)
|
|
679
|
-
const noun = future.value.amount === 1 ? entry.singular : entry.plural;
|
|
680
|
-
return Option.some(`${future.value.amount} ${noun} à venir`);
|
|
681
|
-
}
|
|
841
|
+
if (entry !== void 0) return Option.some(future.value.amount === 1 ? singularRollingCanonical(entry, true) : relativeCountPhrase(future.value.amount, entry, true));
|
|
682
842
|
}
|
|
683
843
|
const trailing = trailingPeriod(range);
|
|
684
844
|
if (Option.isSome(trailing)) {
|
|
685
845
|
const entry = units.find((unit) => unit.unit === trailing.value.unit);
|
|
686
|
-
if (entry !== void 0) return Option.some(relativeCountPhrase(trailing.value.amount, entry, false));
|
|
846
|
+
if (entry !== void 0) return Option.some(trailing.value.amount === 1 ? singularRollingCanonical(entry, false) : relativeCountPhrase(trailing.value.amount, entry, false));
|
|
687
847
|
}
|
|
688
|
-
const periods = [
|
|
689
|
-
|
|
848
|
+
const periods = [
|
|
849
|
+
...staticPeriods,
|
|
850
|
+
...currentYearDatePeriods(range, currentDateLabel),
|
|
851
|
+
...periodsFromPhrases([...datedPeriods(range, months), ...datedQuarterPeriods(range)], parsePeriod)
|
|
852
|
+
];
|
|
853
|
+
return renderPeriodRange(range, [...units.map((entry) => candidate(periodToDateRange(entry.unit), entry.toDate)), ...units.map((entry) => candidate(remainingPeriodRange(entry.unit), entry.remaining))], periods, (period) => `depuis ${period}`, (period) => `avant ${period}`, untilLabel, (period) => `après ${period}`, rangeLabel, (period) => `depuis ${period} jusqu'à maintenant`, (period) => `de maintenant à ${period}`, () => "jusqu'à maintenant", () => "depuis maintenant");
|
|
690
854
|
};
|
|
691
855
|
const normalizeFrench = (input, locale) => normalizeNaturalText(input, locale).replaceAll("’", "'");
|
|
692
856
|
const FrenchContribution = new BaseLanguageContribution({
|