chronolizer 0.3.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 +46 -45
- package/dist/filter/expression.mjs +24 -18
- package/dist/language/model.mjs +1 -1
- package/dist/language/registry.mjs +32 -12
- package/dist/locales/cs.mjs +186 -17
- package/dist/locales/de.mjs +133 -20
- package/dist/locales/en.mjs +151 -35
- package/dist/locales/es.mjs +178 -19
- package/dist/locales/fr.mjs +150 -19
- package/dist/locales/nl.mjs +118 -19
- package/dist/locales/pl.mjs +156 -18
- package/dist/locales/shared.mjs +64 -1
- package/dist/locales/tr.mjs +138 -24
- 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.mjs +5 -4
- package/dist/natural/suggest.mjs +19 -3
- package/dist/natural/suggestion.mjs +11 -5
- package/dist/resolve/resolve.mjs +1 -1
- package/package.json +1 -1
package/dist/locales/es.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 { 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";
|
|
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, periodDay, 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/es.ts
|
|
10
10
|
const months = [
|
|
@@ -21,6 +21,99 @@ const months = [
|
|
|
21
21
|
"noviembre",
|
|
22
22
|
"diciembre"
|
|
23
23
|
];
|
|
24
|
+
const weekdays = [
|
|
25
|
+
"lunes",
|
|
26
|
+
"martes",
|
|
27
|
+
"miércoles",
|
|
28
|
+
"jueves",
|
|
29
|
+
"viernes",
|
|
30
|
+
"sábado",
|
|
31
|
+
"domingo"
|
|
32
|
+
];
|
|
33
|
+
const nextWeekdays = weekdays.flatMap((weekday, day) => [
|
|
34
|
+
{
|
|
35
|
+
phrase: `próximo ${weekday}`,
|
|
36
|
+
day,
|
|
37
|
+
canonical: `el próximo ${weekday}`
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
phrase: `el próximo ${weekday}`,
|
|
41
|
+
day,
|
|
42
|
+
canonical: `el próximo ${weekday}`
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
phrase: `proximo ${weekday}`,
|
|
46
|
+
day,
|
|
47
|
+
canonical: `el próximo ${weekday}`
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
phrase: `el proximo ${weekday}`,
|
|
51
|
+
day,
|
|
52
|
+
canonical: `el próximo ${weekday}`
|
|
53
|
+
}
|
|
54
|
+
]);
|
|
55
|
+
const spanishCountWords = [
|
|
56
|
+
["dos"],
|
|
57
|
+
["tres"],
|
|
58
|
+
["cuatro"],
|
|
59
|
+
["cinco"],
|
|
60
|
+
["seis"],
|
|
61
|
+
["siete"],
|
|
62
|
+
["ocho"],
|
|
63
|
+
["nueve"],
|
|
64
|
+
["diez"],
|
|
65
|
+
["once"],
|
|
66
|
+
["doce"],
|
|
67
|
+
["trece"],
|
|
68
|
+
["catorce"],
|
|
69
|
+
["quince"],
|
|
70
|
+
["dieciséis", "dieciseis"],
|
|
71
|
+
["diecisiete"],
|
|
72
|
+
["dieciocho"],
|
|
73
|
+
["diecinueve"],
|
|
74
|
+
["veinte"]
|
|
75
|
+
];
|
|
76
|
+
const spanishCountOnes = [
|
|
77
|
+
[1, "uno"],
|
|
78
|
+
[2, "dos"],
|
|
79
|
+
[3, "tres"],
|
|
80
|
+
[4, "cuatro"],
|
|
81
|
+
[5, "cinco"],
|
|
82
|
+
[6, "seis"],
|
|
83
|
+
[7, "siete"],
|
|
84
|
+
[8, "ocho"],
|
|
85
|
+
[9, "nueve"]
|
|
86
|
+
];
|
|
87
|
+
const spanishCountAliases = [...sequentialCountAliases(spanishCountWords, 2), ...compoundCountAliases(decimalTens([
|
|
88
|
+
"veinte",
|
|
89
|
+
"treinta",
|
|
90
|
+
"cuarenta",
|
|
91
|
+
"cincuenta",
|
|
92
|
+
"sesenta",
|
|
93
|
+
"setenta",
|
|
94
|
+
"ochenta",
|
|
95
|
+
"noventa"
|
|
96
|
+
]), spanishCountOnes, (ten, one, amount) => {
|
|
97
|
+
if (amount === 21) return [
|
|
98
|
+
"veintiuno",
|
|
99
|
+
"veintiún",
|
|
100
|
+
"veintiun",
|
|
101
|
+
"veintiuna"
|
|
102
|
+
];
|
|
103
|
+
if (amount === 22) return ["veintidós", "veintidos"];
|
|
104
|
+
if (amount === 23) return ["veintitrés", "veintitres"];
|
|
105
|
+
if (amount === 26) return ["veintiséis", "veintiseis"];
|
|
106
|
+
if (amount < 30) return [`veinti${one}`];
|
|
107
|
+
return (one === "uno" ? [
|
|
108
|
+
one,
|
|
109
|
+
"un",
|
|
110
|
+
"una"
|
|
111
|
+
] : [one]).map((variant) => `${ten} y ${variant}`);
|
|
112
|
+
})];
|
|
113
|
+
const normalizeSpanishCounts = compileCountAliasNormalizer(spanishCountAliases);
|
|
114
|
+
const spanishCountVocabulary = new Set(countAliasVocabulary(spanishCountAliases));
|
|
115
|
+
const correctSpanish = (input, vocabulary) => correctWhitespaceSeparatedText(input, vocabulary, spanishCountVocabulary);
|
|
116
|
+
const normalizeSpanish = (input, locale) => normalizeSpanishCounts(normalizeNaturalText(input, locale));
|
|
24
117
|
const monthAbbreviations = [
|
|
25
118
|
["ene"],
|
|
26
119
|
["feb"],
|
|
@@ -513,13 +606,13 @@ const parseQuarter = (input) => {
|
|
|
513
606
|
const quarter = quarterNumber(textAt(standalone.value, 1));
|
|
514
607
|
return quarter === void 0 ? Option.none() : Option.some(quarterOfRelativeYear(quarter, 0, `T${quarter}`));
|
|
515
608
|
};
|
|
516
|
-
const
|
|
517
|
-
const
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
if (Option.isSome(
|
|
609
|
+
const parseDatedPeriod = (input) => {
|
|
610
|
+
const knownPeriod = Option.firstSomeOf([
|
|
611
|
+
absoluteDatePeriod(input, "es"),
|
|
612
|
+
parseNamedDate(input),
|
|
613
|
+
parseQuarter(input)
|
|
614
|
+
]);
|
|
615
|
+
if (Option.isSome(knownPeriod)) return knownPeriod;
|
|
523
616
|
const yearMatch = String$1.match(/^(?:(?:el )?año )?(\d{4})$/u)(input);
|
|
524
617
|
if (Option.isSome(yearMatch)) {
|
|
525
618
|
const year = validYear(textAt(yearMatch.value, 1));
|
|
@@ -531,6 +624,26 @@ const parseBasePeriod = (input) => {
|
|
|
531
624
|
const year = validYear(textAt(monthYear.value, 2));
|
|
532
625
|
if (month !== void 0 && year !== void 0) return Option.some(fixedMonthPeriod(year, month, `${textAt(months, month - 1)} de ${year}`));
|
|
533
626
|
}
|
|
627
|
+
return Option.none();
|
|
628
|
+
};
|
|
629
|
+
const parseBasePeriod = (input) => {
|
|
630
|
+
const datedPeriod = parseDatedPeriod(input);
|
|
631
|
+
if (Option.isSome(datedPeriod)) return datedPeriod;
|
|
632
|
+
const prefixedRelativeMonth = String$1.match(/^(pasado|próximo|proximo|este) ([a-záéíóúñ]+\.?)$/u)(input);
|
|
633
|
+
const suffixedRelativeMonth = String$1.match(/^([a-záéíóúñ]+\.?) (pasado|próximo|proximo)$/u)(input);
|
|
634
|
+
const shortRelativeMonth = Option.firstSomeOf([prefixedRelativeMonth, suffixedRelativeMonth]);
|
|
635
|
+
if (Option.isSome(shortRelativeMonth)) {
|
|
636
|
+
const prefixed = Option.isSome(prefixedRelativeMonth);
|
|
637
|
+
const month = monthNumber(textAt(shortRelativeMonth.value, prefixed ? 2 : 1));
|
|
638
|
+
if (month !== void 0) {
|
|
639
|
+
const modifier = textAt(shortRelativeMonth.value, prefixed ? 1 : 2);
|
|
640
|
+
const direction = relativeYearDirection(modifier);
|
|
641
|
+
let canonicalModifier = "este";
|
|
642
|
+
if (direction < 0) canonicalModifier = "pasado";
|
|
643
|
+
if (direction > 0) canonicalModifier = "próximo";
|
|
644
|
+
return Option.some(monthOfRelativeYear(month, direction, `${canonicalModifier} ${textAt(months, month - 1)}`));
|
|
645
|
+
}
|
|
646
|
+
}
|
|
534
647
|
const relativeMonth = String$1.match(/^([a-záéíóúñ]+\.?) (del año pasado|del ano pasado|del próximo año|del proximo año|de este año|de este ano)$/u)(input);
|
|
535
648
|
if (Option.isSome(relativeMonth)) {
|
|
536
649
|
const month = monthNumber(textAt(relativeMonth.value, 1));
|
|
@@ -554,13 +667,31 @@ const parseBasePeriod = (input) => {
|
|
|
554
667
|
if (["el próximo fin de semana", "el proximo fin de semana"].includes(input)) return Option.some(relativeWeekend(1, "el próximo fin de semana"));
|
|
555
668
|
if (input === "el fin de semana anterior al pasado") return Option.some(relativeWeekend(-2, input));
|
|
556
669
|
if (input === "el fin de semana después del próximo") return Option.some(relativeWeekend(2, input));
|
|
557
|
-
|
|
670
|
+
const weekday = nextWeekdays.find((entry) => entry.phrase === input);
|
|
671
|
+
return weekday === void 0 ? Option.none() : Option.some(relativeWeekday(weekday.day, 1, weekday.canonical));
|
|
558
672
|
};
|
|
559
673
|
const parsePeriod = (input) => {
|
|
674
|
+
const shifted = String$1.match(/^(.+) (hace|dentro de|en) ([1-9]\d*) (día|dia|días|dias|semana|semanas|mes|meses|trimestre|trimestres|año|años|ano|anos)$/u)(input);
|
|
675
|
+
if (Option.isSome(shifted)) {
|
|
676
|
+
const amount = parseTrailingCount(textAt(shifted.value, 3));
|
|
677
|
+
const alias = unitAliases.find((unit) => unit[0] === textAt(shifted.value, 4));
|
|
678
|
+
const entry = alias === void 0 ? void 0 : units.find((unit) => unit.unit === alias[1]);
|
|
679
|
+
const period = parsePeriod(textAt(shifted.value, 1));
|
|
680
|
+
if (Option.isSome(amount) && entry !== void 0 && Option.isSome(period)) {
|
|
681
|
+
const past = textAt(shifted.value, 2) === "hace";
|
|
682
|
+
const direction = past ? -amount.value : amount.value;
|
|
683
|
+
const noun = amount.value === 1 ? entry.singular : entry.plural;
|
|
684
|
+
const canonical = `${period.value.canonical} ${past ? "hace" : "dentro de"} ${amount.value} ${noun}`;
|
|
685
|
+
return Option.some(shiftPeriod(period.value, direction, entry.unit, canonical));
|
|
686
|
+
}
|
|
687
|
+
}
|
|
560
688
|
const edge = String$1.match(/^(?:el )?(inicio|comienzo|principio|fin|final)(?: de| del) (.+)$/u)(input);
|
|
561
689
|
if (Option.isSome(edge)) {
|
|
562
690
|
const edgeName = textAt(edge.value, 1);
|
|
563
|
-
const
|
|
691
|
+
const periodText = textAt(edge.value, 2);
|
|
692
|
+
const basePeriod = parseBasePeriod(periodText);
|
|
693
|
+
const implicit = units.find((entry) => entry.singular === periodText);
|
|
694
|
+
const period = Option.isSome(basePeriod) || implicit === void 0 ? basePeriod : Option.some(relativePeriod(implicit.unit, 0, implicit.current));
|
|
564
695
|
if (Option.isSome(period)) {
|
|
565
696
|
const isEnd = edgeName === "fin" || edgeName === "final";
|
|
566
697
|
const canonical = `${isEnd ? "final" : "inicio"} ${withDe(period.value.canonical)}`;
|
|
@@ -574,7 +705,8 @@ const parsePeriod = (input) => {
|
|
|
574
705
|
"todo el ",
|
|
575
706
|
"toda la "
|
|
576
707
|
].find((prefix) => input.startsWith(prefix));
|
|
577
|
-
|
|
708
|
+
const base = parseBasePeriod(wrapper === void 0 ? input : input.slice(wrapper.length));
|
|
709
|
+
return Option.isSome(base) ? base : parseCalendarOffset(input);
|
|
578
710
|
};
|
|
579
711
|
const countedUnit = (value, amount) => {
|
|
580
712
|
const alias = unitAliases.find((entry) => entry[0] === value && entry[2] === (amount !== 1));
|
|
@@ -609,7 +741,7 @@ const parseCalendarOffset = (input) => {
|
|
|
609
741
|
const noun = amount.value === 1 ? entry.singular : entry.plural;
|
|
610
742
|
const canonical = `${isPast ? "hace" : "dentro de"} ${quantity} ${noun}`;
|
|
611
743
|
const direction = isPast ? -amount.value : amount.value;
|
|
612
|
-
return Option.some(
|
|
744
|
+
return Option.some(relativePeriod(entry.unit, direction, canonical));
|
|
613
745
|
};
|
|
614
746
|
const rollingCanonical = (entry, amount, future) => {
|
|
615
747
|
if (amount > 1) return `${rollingAdjective(entry, future, true)} ${amount} ${entry.plural}`;
|
|
@@ -617,7 +749,7 @@ const rollingCanonical = (entry, amount, future) => {
|
|
|
617
749
|
return future ? `desde ahora durante ${article} ${entry.singular}` : `desde hace ${article} ${entry.singular}`;
|
|
618
750
|
};
|
|
619
751
|
const rollingCandidate = (entry, amount, future) => candidate(future ? futureRange(amount, entry.unit) : trailingRange(amount, entry.unit), rollingCanonical(entry, amount, future));
|
|
620
|
-
const
|
|
752
|
+
const parseRollingSince = (input) => {
|
|
621
753
|
const since = String$1.match(/^desde hace ([1-9]\d*|un|una) (día|dia|días|dias|semana|semanas|mes|meses|trimestre|trimestres|año|años|ano|anos)$/u)(input);
|
|
622
754
|
if (Option.isSome(since)) {
|
|
623
755
|
const amount = writtenAmount(textAt(since.value, 1));
|
|
@@ -627,6 +759,9 @@ const parseRollingPeriod = (input) => {
|
|
|
627
759
|
if (entry !== void 0 && (amountText !== "un" && amountText !== "una" || agreesWithIndefiniteArticle(entry, amountText))) return Option.some(rollingCandidate(entry, amount.value, false));
|
|
628
760
|
}
|
|
629
761
|
}
|
|
762
|
+
return Option.none();
|
|
763
|
+
};
|
|
764
|
+
const parseSingularRolling = (input) => {
|
|
630
765
|
const futureSingular = String$1.match(/^desde ahora durante (un|una) (día|dia|semana|mes|trimestre|año|ano)$/u)(input);
|
|
631
766
|
if (Option.isSome(futureSingular)) {
|
|
632
767
|
const entry = countedUnit(textAt(futureSingular.value, 2), 1);
|
|
@@ -644,6 +779,9 @@ const parseRollingPeriod = (input) => {
|
|
|
644
779
|
const modifier = textAt(nextSingular.value, 2);
|
|
645
780
|
if (entry !== void 0 && agreesWithArticle(entry, textAt(nextSingular.value, 1)) && agreesWithSingularModifier(entry, modifier, true)) return Option.some(rollingCandidate(entry, 1, true));
|
|
646
781
|
}
|
|
782
|
+
return Option.none();
|
|
783
|
+
};
|
|
784
|
+
const parseCountedRolling = (input) => {
|
|
647
785
|
const counted = String$1.match(/^(?:(?:durante|en) )?(?:(los|las) )?(últimos|ultimos|últimas|ultimas|pasados|pasadas|anteriores|próximos|proximos|próximas|proximas|siguientes) ([1-9]\d*) (día|dia|días|dias|semana|semanas|mes|meses|trimestre|trimestres|año|años|ano|anos)$/u)(input);
|
|
648
786
|
if (Option.isSome(counted)) {
|
|
649
787
|
const amount = parseTrailingCount(textAt(counted.value, 3));
|
|
@@ -661,6 +799,11 @@ const parseRollingPeriod = (input) => {
|
|
|
661
799
|
const entry = countedUnit(textAt(bare.value, 2), amount.value);
|
|
662
800
|
return entry === void 0 ? Option.none() : Option.some(rollingCandidate(entry, amount.value, false));
|
|
663
801
|
};
|
|
802
|
+
const parseRollingPeriod = (input) => Option.firstSomeOf([
|
|
803
|
+
parseRollingSince(input),
|
|
804
|
+
parseSingularRolling(input),
|
|
805
|
+
parseCountedRolling(input)
|
|
806
|
+
]);
|
|
664
807
|
const parseElidedDateRange = (input) => {
|
|
665
808
|
const joined = String$1.match(/^(?:(?:del|desde(?: el)?) ([0-3]?\d) (?:al|hasta(?: el)?)|entre(?: el)? ([0-3]?\d) y(?: el)?) ([0-3]?\d) (.+)$/u)(input);
|
|
666
809
|
const dashed = String$1.match(/^([0-3]?\d)[–—-]([0-3]?\d) (.+)$/u)(input);
|
|
@@ -698,8 +841,6 @@ const parseSpanish = (input) => {
|
|
|
698
841
|
"a partir de ahora",
|
|
699
842
|
"de ahora en adelante"
|
|
700
843
|
].includes(input)) return Option.some(candidate(fromNowRange(), "desde ahora"));
|
|
701
|
-
const offset = parseCalendarOffset(input);
|
|
702
|
-
if (Option.isSome(offset)) return offset;
|
|
703
844
|
const rolling = parseRollingPeriod(input);
|
|
704
845
|
if (Option.isSome(rolling)) return rolling;
|
|
705
846
|
const toDate = toDatePhrases.find((entry) => entry.phrase === input);
|
|
@@ -714,7 +855,8 @@ const parseSpanish = (input) => {
|
|
|
714
855
|
"desde ahora hasta ",
|
|
715
856
|
"desde hoy hasta ",
|
|
716
857
|
"entre hoy y ",
|
|
717
|
-
"entre hoy y el "
|
|
858
|
+
"entre hoy y el ",
|
|
859
|
+
"ahora hasta "
|
|
718
860
|
], parsePeriod, (period) => `desde ${period} hasta ahora`, (period) => `desde ahora hasta ${period}`);
|
|
719
861
|
if (Option.isSome(nowBounded)) return nowBounded;
|
|
720
862
|
const bounded = joinedPeriodCandidate(input, [
|
|
@@ -769,9 +911,14 @@ const staticPeriodPhrases = [
|
|
|
769
911
|
]),
|
|
770
912
|
...months.flatMap((month) => [
|
|
771
913
|
month,
|
|
914
|
+
`pasado ${month}`,
|
|
915
|
+
`próximo ${month}`,
|
|
916
|
+
`${month} pasado`,
|
|
917
|
+
`${month} próximo`,
|
|
772
918
|
`${month} del año pasado`,
|
|
773
919
|
`${month} del próximo año`
|
|
774
|
-
])
|
|
920
|
+
]),
|
|
921
|
+
...nextWeekdays.map((entry) => entry.phrase)
|
|
775
922
|
];
|
|
776
923
|
const staticPeriods = periodsFromPhrases(staticPeriodPhrases, parsePeriod);
|
|
777
924
|
const boundaryPrefixes = [
|
|
@@ -830,6 +977,17 @@ const suggestSpanish = (input, limit) => {
|
|
|
830
977
|
], limit);
|
|
831
978
|
};
|
|
832
979
|
const renderSpanish = (range) => {
|
|
980
|
+
const shifted = decomposeShiftedPeriodRange(range);
|
|
981
|
+
if (Option.isSome(shifted)) {
|
|
982
|
+
const base = renderSpanish(shifted.value.baseRange);
|
|
983
|
+
const entry = units.find((unit) => unit.unit === shifted.value.unit);
|
|
984
|
+
if (Option.isSome(base) && entry !== void 0) {
|
|
985
|
+
const amount = Math.abs(shifted.value.amount);
|
|
986
|
+
const noun = amount === 1 ? entry.singular : entry.plural;
|
|
987
|
+
const direction = shifted.value.amount < 0 ? "hace" : "dentro de";
|
|
988
|
+
return Option.some(`${base.value} ${direction} ${amount} ${noun}`);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
833
991
|
const offset = calendarPeriodOffset(range);
|
|
834
992
|
if (Option.isSome(offset) && Math.abs(offset.value.amount) > 1) {
|
|
835
993
|
const entry = units.find((unit) => unit.unit === offset.value.unit);
|
|
@@ -860,6 +1018,7 @@ const SpanishContribution = new BaseLanguageContribution({
|
|
|
860
1018
|
locale: "es",
|
|
861
1019
|
vocabulary: [
|
|
862
1020
|
...months,
|
|
1021
|
+
...weekdays,
|
|
863
1022
|
...monthAbbreviations.flatMap((aliases) => aliases),
|
|
864
1023
|
...units.flatMap((entry) => [
|
|
865
1024
|
entry.singular,
|
|
@@ -893,8 +1052,8 @@ const SpanishContribution = new BaseLanguageContribution({
|
|
|
893
1052
|
"resto",
|
|
894
1053
|
"últimos"
|
|
895
1054
|
],
|
|
896
|
-
normalize:
|
|
897
|
-
correct:
|
|
1055
|
+
normalize: normalizeSpanish,
|
|
1056
|
+
correct: correctSpanish,
|
|
898
1057
|
parseExact: parseSpanish,
|
|
899
1058
|
suggest: suggestSpanish,
|
|
900
1059
|
render: renderSpanish
|
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 { 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";
|
|
7
|
+
import { absoluteDatePeriod, calendarPeriodOffset, candidate, compileCountAliasNormalizer, countAliasVocabulary, currentYearDatePeriods, datedPeriods, datedQuarterPeriods, decomposeShiftedPeriodRange, fixedDatePeriod, fixedMonthPeriod, fixedQuarterPeriod, fixedYearPeriod, fromNowRange, futurePeriod, futureRange, isoDate, joinedNowCandidate, joinedPeriodCandidate, monthOfRelativeYear, namedCurrentYearDatePeriod, namedDatePeriod, openBoundaryCandidate, parseTrailingCount, periodBoundaryCandidate, periodDay, 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/fr.ts
|
|
10
10
|
const months = [
|
|
@@ -21,6 +21,89 @@ const months = [
|
|
|
21
21
|
"novembre",
|
|
22
22
|
"décembre"
|
|
23
23
|
];
|
|
24
|
+
const weekdays = [
|
|
25
|
+
"lundi",
|
|
26
|
+
"mardi",
|
|
27
|
+
"mercredi",
|
|
28
|
+
"jeudi",
|
|
29
|
+
"vendredi",
|
|
30
|
+
"samedi",
|
|
31
|
+
"dimanche"
|
|
32
|
+
];
|
|
33
|
+
const nextWeekdayPhrases = weekdays.map((weekday) => `${weekday} prochain`);
|
|
34
|
+
const frenchCountWords = [
|
|
35
|
+
["deux"],
|
|
36
|
+
["trois"],
|
|
37
|
+
["quatre"],
|
|
38
|
+
["cinq"],
|
|
39
|
+
["six"],
|
|
40
|
+
["sept"],
|
|
41
|
+
["huit"],
|
|
42
|
+
["neuf"],
|
|
43
|
+
["dix"],
|
|
44
|
+
["onze"],
|
|
45
|
+
["douze"],
|
|
46
|
+
["treize"],
|
|
47
|
+
["quatorze"],
|
|
48
|
+
["quinze"],
|
|
49
|
+
["seize"],
|
|
50
|
+
["dix-sept"],
|
|
51
|
+
["dix-huit"],
|
|
52
|
+
["dix-neuf"],
|
|
53
|
+
["vingt"]
|
|
54
|
+
];
|
|
55
|
+
const frenchCompoundCountAliases = () => {
|
|
56
|
+
const words = [
|
|
57
|
+
"",
|
|
58
|
+
"un",
|
|
59
|
+
"deux",
|
|
60
|
+
"trois",
|
|
61
|
+
"quatre",
|
|
62
|
+
"cinq",
|
|
63
|
+
"six",
|
|
64
|
+
"sept",
|
|
65
|
+
"huit",
|
|
66
|
+
"neuf",
|
|
67
|
+
"dix",
|
|
68
|
+
"onze",
|
|
69
|
+
"douze",
|
|
70
|
+
"treize",
|
|
71
|
+
"quatorze",
|
|
72
|
+
"quinze",
|
|
73
|
+
"seize",
|
|
74
|
+
"dix-sept",
|
|
75
|
+
"dix-huit",
|
|
76
|
+
"dix-neuf"
|
|
77
|
+
];
|
|
78
|
+
const aliases = [];
|
|
79
|
+
for (let amount = 21; amount <= 99; amount += 1) {
|
|
80
|
+
const tens = Math.floor(amount / 10);
|
|
81
|
+
const remainder = amount % 10;
|
|
82
|
+
let phrase = "";
|
|
83
|
+
if (tens <= 6) {
|
|
84
|
+
const tensWord = [
|
|
85
|
+
"",
|
|
86
|
+
"",
|
|
87
|
+
"vingt",
|
|
88
|
+
"trente",
|
|
89
|
+
"quarante",
|
|
90
|
+
"cinquante",
|
|
91
|
+
"soixante"
|
|
92
|
+
][tens];
|
|
93
|
+
if (tensWord === void 0) continue;
|
|
94
|
+
phrase = remainder === 1 ? `${tensWord} et un` : `${tensWord}-${words[remainder] ?? ""}`;
|
|
95
|
+
} else if (tens === 7) phrase = remainder === 1 ? "soixante et onze" : `soixante-${words[10 + remainder] ?? ""}`;
|
|
96
|
+
else if (tens === 8) phrase = remainder === 0 ? "quatre-vingts" : `quatre-vingt-${words[remainder] ?? ""}`;
|
|
97
|
+
else phrase = `quatre-vingt-${words[10 + remainder] ?? ""}`;
|
|
98
|
+
aliases.push([phrase, amount], [phrase.replaceAll("-", " "), amount]);
|
|
99
|
+
if (amount % 10 === 1) aliases.push([phrase.replace(/un$/u, "une"), amount]);
|
|
100
|
+
}
|
|
101
|
+
return aliases;
|
|
102
|
+
};
|
|
103
|
+
const frenchCountAliases = [...sequentialCountAliases(frenchCountWords, 2), ...frenchCompoundCountAliases()];
|
|
104
|
+
const normalizeFrenchCounts = compileCountAliasNormalizer(frenchCountAliases);
|
|
105
|
+
const frenchCountVocabulary = new Set(countAliasVocabulary(frenchCountAliases));
|
|
106
|
+
const correctFrench = (input, vocabulary) => correctWhitespaceSeparatedText(input, vocabulary, frenchCountVocabulary);
|
|
24
107
|
const monthAbbreviations = [
|
|
25
108
|
["jan", "janv"],
|
|
26
109
|
[
|
|
@@ -461,13 +544,13 @@ const parseQuarter = (input) => {
|
|
|
461
544
|
const quarter = quarterNumber(textAt(standalone.value, 1));
|
|
462
545
|
return quarter === void 0 ? Option.none() : Option.some(quarterOfRelativeYear(quarter, 0, `T${quarter}`));
|
|
463
546
|
};
|
|
464
|
-
const
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
if (Option.isSome(
|
|
547
|
+
const parseDatedPeriod = (input) => {
|
|
548
|
+
const knownPeriod = Option.firstSomeOf([
|
|
549
|
+
absoluteDatePeriod(input, "fr"),
|
|
550
|
+
parseNamedDate(input),
|
|
551
|
+
parseQuarter(input)
|
|
552
|
+
]);
|
|
553
|
+
if (Option.isSome(knownPeriod)) return knownPeriod;
|
|
471
554
|
const yearMatch = String$1.match(/^(?:(?:l')?année |annee )?(\d{4})$/u)(input);
|
|
472
555
|
if (Option.isSome(yearMatch)) {
|
|
473
556
|
const year = validYear(textAt(yearMatch.value, 1));
|
|
@@ -479,6 +562,20 @@ const parseBasePeriod = (input) => {
|
|
|
479
562
|
const year = validYear(textAt(monthYear.value, 2));
|
|
480
563
|
if (month !== void 0 && year !== void 0) return Option.some(fixedMonthPeriod(year, month, `${textAt(months, month - 1)} ${year}`));
|
|
481
564
|
}
|
|
565
|
+
return Option.none();
|
|
566
|
+
};
|
|
567
|
+
const parseBasePeriod = (input) => {
|
|
568
|
+
const datedPeriod = parseDatedPeriod(input);
|
|
569
|
+
if (Option.isSome(datedPeriod)) return datedPeriod;
|
|
570
|
+
const suffixedRelativeMonth = String$1.match(/^([a-zàâçéèêëîïôûùüÿœ]+\.?) (dernier|prochain)$/u)(input);
|
|
571
|
+
if (Option.isSome(suffixedRelativeMonth)) {
|
|
572
|
+
const month = monthNumber(textAt(suffixedRelativeMonth.value, 1));
|
|
573
|
+
if (month !== void 0) {
|
|
574
|
+
const modifier = textAt(suffixedRelativeMonth.value, 2);
|
|
575
|
+
const direction = modifier === "dernier" ? -1 : 1;
|
|
576
|
+
return Option.some(monthOfRelativeYear(month, direction, `${textAt(months, month - 1)} ${modifier}`));
|
|
577
|
+
}
|
|
578
|
+
}
|
|
482
579
|
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);
|
|
483
580
|
if (Option.isSome(relativeMonth)) {
|
|
484
581
|
const month = monthNumber(textAt(relativeMonth.value, 1));
|
|
@@ -509,13 +606,32 @@ const parseBasePeriod = (input) => {
|
|
|
509
606
|
].includes(input)) return Option.some(relativeWeekend(1, "le week-end prochain"));
|
|
510
607
|
if (input === "l'avant-dernier week-end") return Option.some(relativeWeekend(-2, input));
|
|
511
608
|
if (input === "le week-end après le prochain") return Option.some(relativeWeekend(2, input));
|
|
512
|
-
|
|
609
|
+
const weekday = nextWeekdayPhrases.indexOf(input);
|
|
610
|
+
return weekday === -1 ? Option.none() : Option.some(relativeWeekday(weekday, 1, nextWeekdayPhrases[weekday] ?? input));
|
|
513
611
|
};
|
|
514
612
|
const parsePeriod = (input) => {
|
|
613
|
+
const shifted = String$1.match(/^(.+) (il y a|dans) ([1-9]\d*) (jour|jours|semaine|semaines|mois|trimestre|trimestres|an|ans|année|annee|années|annees)$/u)(input);
|
|
614
|
+
if (Option.isSome(shifted)) {
|
|
615
|
+
const amount = parseTrailingCount(textAt(shifted.value, 3));
|
|
616
|
+
const alias = unitAliases.find((unit) => unit[0] === textAt(shifted.value, 4));
|
|
617
|
+
const entry = alias === void 0 ? void 0 : units.find((unit) => unit.unit === alias[1]);
|
|
618
|
+
const period = parsePeriod(textAt(shifted.value, 1));
|
|
619
|
+
if (Option.isSome(amount) && entry !== void 0 && Option.isSome(period)) {
|
|
620
|
+
const past = textAt(shifted.value, 2) === "il y a";
|
|
621
|
+
const direction = past ? -amount.value : amount.value;
|
|
622
|
+
const noun = amount.value === 1 ? entry.singular : entry.plural;
|
|
623
|
+
const canonical = `${period.value.canonical} ${past ? "il y a" : "dans"} ${amount.value} ${noun}`;
|
|
624
|
+
return Option.some(shiftPeriod(period.value, direction, entry.unit, canonical));
|
|
625
|
+
}
|
|
626
|
+
}
|
|
515
627
|
const edge = String$1.match(/^(?:le |la |l')?(début|debut|commencement|fin)(?: du | de la | de l'| de )(.+)$/u)(input);
|
|
516
628
|
if (Option.isSome(edge)) {
|
|
517
629
|
const edgeName = textAt(edge.value, 1);
|
|
518
|
-
const
|
|
630
|
+
const periodText = textAt(edge.value, 2);
|
|
631
|
+
const basePeriod = parseBasePeriod(periodText);
|
|
632
|
+
const implicitUnit = unitAliases.find((entry) => entry[0] === periodText)?.[1];
|
|
633
|
+
const implicit = units.find((entry) => entry.unit === implicitUnit);
|
|
634
|
+
const period = Option.isSome(basePeriod) || implicit === void 0 ? basePeriod : Option.some(relativePeriod(implicit.unit, 0, implicit.current));
|
|
519
635
|
if (Option.isSome(period)) {
|
|
520
636
|
const isEnd = edgeName === "fin";
|
|
521
637
|
const canonical = `${isEnd ? "fin" : "début"} ${withDe(period.value.canonical)}`;
|
|
@@ -529,7 +645,8 @@ const parsePeriod = (input) => {
|
|
|
529
645
|
"tout le ",
|
|
530
646
|
"toute la "
|
|
531
647
|
].find((prefix) => input.startsWith(prefix));
|
|
532
|
-
|
|
648
|
+
const base = parseBasePeriod(wrapper === void 0 ? input : input.slice(wrapper.length));
|
|
649
|
+
return Option.isSome(base) ? base : parseCalendarOffset(input);
|
|
533
650
|
};
|
|
534
651
|
const countedUnit = (value, amount) => {
|
|
535
652
|
const plural = value === "mois" || value.endsWith("s");
|
|
@@ -623,7 +740,7 @@ const singularCalendarOffsets = units.flatMap((entry) => {
|
|
|
623
740
|
});
|
|
624
741
|
const parseCalendarOffset = (input) => {
|
|
625
742
|
const singular = singularCalendarOffsets.find((entry) => entry.phrase === input);
|
|
626
|
-
if (singular !== void 0) return Option.some(
|
|
743
|
+
if (singular !== void 0) return Option.some(relativePeriod(singular.entry.unit, singular.direction, singular.phrase));
|
|
627
744
|
const past = String$1.match(calendarPastPattern)(input);
|
|
628
745
|
const future = String$1.match(calendarFuturePattern)(input);
|
|
629
746
|
const match = Option.firstSomeOf([past, future]);
|
|
@@ -635,7 +752,7 @@ const parseCalendarOffset = (input) => {
|
|
|
635
752
|
const direction = Option.isSome(past) ? -amount.value : amount.value;
|
|
636
753
|
const noun = amount.value === 1 ? entry.singular : entry.plural;
|
|
637
754
|
const canonical = direction < 0 ? `il y a ${amount.value} ${noun}` : `dans ${amount.value} ${noun}`;
|
|
638
|
-
return Option.some(
|
|
755
|
+
return Option.some(relativePeriod(entry.unit, direction, canonical));
|
|
639
756
|
};
|
|
640
757
|
const parseRollingPeriod = (input) => {
|
|
641
758
|
const singular = singularRollingPhrases.find((entry) => entry.phrase === input);
|
|
@@ -708,8 +825,6 @@ const parseFrench = (input) => {
|
|
|
708
825
|
"à partir de maintenant",
|
|
709
826
|
"désormais"
|
|
710
827
|
].includes(input)) return Option.some(candidate(fromNowRange(), "depuis maintenant"));
|
|
711
|
-
const offset = parseCalendarOffset(input);
|
|
712
|
-
if (Option.isSome(offset)) return offset;
|
|
713
828
|
const rolling = parseRollingPeriod(input);
|
|
714
829
|
if (Option.isSome(rolling)) return rolling;
|
|
715
830
|
const toDate = toDatePhrases.find((entry) => entry.phrase === input);
|
|
@@ -723,7 +838,8 @@ const parseFrench = (input) => {
|
|
|
723
838
|
], [
|
|
724
839
|
"d'aujourd'hui à ",
|
|
725
840
|
"de maintenant à ",
|
|
726
|
-
"entre aujourd'hui et "
|
|
841
|
+
"entre aujourd'hui et ",
|
|
842
|
+
"maintenant à "
|
|
727
843
|
], parsePeriod, (period) => `depuis ${period} jusqu'à maintenant`, (period) => `de maintenant à ${period}`);
|
|
728
844
|
if (Option.isSome(nowBounded)) return nowBounded;
|
|
729
845
|
const bounded = joinedPeriodCandidate(input, [
|
|
@@ -775,9 +891,12 @@ const staticPeriodPhrases = [
|
|
|
775
891
|
]),
|
|
776
892
|
...months.flatMap((month) => [
|
|
777
893
|
month,
|
|
894
|
+
`${month} dernier`,
|
|
895
|
+
`${month} prochain`,
|
|
778
896
|
`${month} de l'année dernière`,
|
|
779
897
|
`${month} de l'année prochaine`
|
|
780
|
-
])
|
|
898
|
+
]),
|
|
899
|
+
...nextWeekdayPhrases
|
|
781
900
|
];
|
|
782
901
|
const staticPeriods = periodsFromPhrases(staticPeriodPhrases, parsePeriod);
|
|
783
902
|
const boundaryPrefixes = [
|
|
@@ -826,6 +945,17 @@ const suggestFrench = (input, limit) => {
|
|
|
826
945
|
], limit);
|
|
827
946
|
};
|
|
828
947
|
const renderFrench = (range) => {
|
|
948
|
+
const shifted = decomposeShiftedPeriodRange(range);
|
|
949
|
+
if (Option.isSome(shifted)) {
|
|
950
|
+
const base = renderFrench(shifted.value.baseRange);
|
|
951
|
+
const entry = units.find((unit) => unit.unit === shifted.value.unit);
|
|
952
|
+
if (Option.isSome(base) && entry !== void 0) {
|
|
953
|
+
const amount = Math.abs(shifted.value.amount);
|
|
954
|
+
const noun = amount === 1 ? entry.singular : entry.plural;
|
|
955
|
+
const direction = shifted.value.amount < 0 ? "il y a" : "dans";
|
|
956
|
+
return Option.some(`${base.value} ${direction} ${amount} ${noun}`);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
829
959
|
const offset = calendarPeriodOffset(range);
|
|
830
960
|
if (Option.isSome(offset) && Math.abs(offset.value.amount) > 1) {
|
|
831
961
|
const entry = units.find((unit) => unit.unit === offset.value.unit);
|
|
@@ -852,11 +982,12 @@ const renderFrench = (range) => {
|
|
|
852
982
|
];
|
|
853
983
|
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");
|
|
854
984
|
};
|
|
855
|
-
const normalizeFrench = (input, locale) => normalizeNaturalText(input, locale).replaceAll("’", "'");
|
|
985
|
+
const normalizeFrench = (input, locale) => normalizeFrenchCounts(normalizeNaturalText(input, locale).replaceAll("’", "'"));
|
|
856
986
|
const FrenchContribution = new BaseLanguageContribution({
|
|
857
987
|
locale: "fr",
|
|
858
988
|
vocabulary: [
|
|
859
989
|
...months,
|
|
990
|
+
...weekdays,
|
|
860
991
|
...monthAbbreviations.flatMap((aliases) => aliases),
|
|
861
992
|
...units.flatMap((entry) => [
|
|
862
993
|
entry.singular,
|
|
@@ -887,7 +1018,7 @@ const FrenchContribution = new BaseLanguageContribution({
|
|
|
887
1018
|
"jusqu'à"
|
|
888
1019
|
],
|
|
889
1020
|
normalize: normalizeFrench,
|
|
890
|
-
correct:
|
|
1021
|
+
correct: correctFrench,
|
|
891
1022
|
parseExact: parseFrench,
|
|
892
1023
|
suggest: suggestFrench,
|
|
893
1024
|
render: renderFrench
|