chronolizer 0.1.0 → 0.2.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 +70 -7
- package/dist/ast/constructors.d.mts +140 -0
- package/dist/ast/constructors.mjs +25 -0
- package/dist/ast/fold.d.mts +12 -0
- package/dist/ast/fold.mjs +51 -0
- package/dist/ast/normalize.d.mts +42 -0
- package/dist/ast/normalize.mjs +32 -0
- package/dist/ast/schemas.d.mts +87 -0
- package/dist/ast/schemas.mjs +74 -0
- package/dist/filter/codec.d.mts +92 -0
- package/dist/filter/codec.mjs +48 -0
- package/dist/filter/errors.d.mts +14 -0
- package/dist/filter/errors.mjs +10 -0
- package/dist/filter/expression.d.mts +8 -0
- package/dist/filter/expression.mjs +93 -0
- package/dist/filter/schema.d.mts +13 -0
- package/dist/filter/schema.mjs +11 -0
- package/dist/filter/transformation.d.mts +37 -0
- package/dist/filter/transformation.mjs +20 -0
- package/dist/index.d.mts +22 -969
- package/dist/index.mjs +22 -2305
- package/dist/language/errors.d.mts +38 -0
- package/dist/language/errors.mjs +30 -0
- package/dist/language/model.d.mts +239 -0
- package/dist/language/model.mjs +53 -0
- package/dist/language/registry.d.mts +17 -0
- package/dist/language/registry.mjs +134 -0
- package/dist/locales/cs.d.mts +10 -0
- package/dist/locales/cs.mjs +693 -0
- package/dist/locales/de.d.mts +10 -0
- package/dist/locales/de.mjs +828 -0
- package/dist/locales/en.d.mts +10 -0
- package/dist/locales/en.mjs +550 -0
- package/dist/locales/es.d.mts +10 -0
- package/dist/locales/es.mjs +660 -0
- package/dist/locales/fr.d.mts +10 -0
- package/dist/locales/fr.mjs +737 -0
- package/dist/locales/nl.d.mts +10 -0
- package/dist/locales/nl.mjs +652 -0
- package/dist/locales/pl.d.mts +10 -0
- package/dist/locales/pl.mjs +711 -0
- package/dist/locales/shared.mjs +289 -0
- package/dist/locales/tr.d.mts +10 -0
- package/dist/locales/tr.mjs +660 -0
- package/dist/natural/correction.d.mts +13 -0
- package/dist/natural/correction.mjs +73 -0
- package/dist/natural/format.d.mts +47 -0
- package/dist/natural/format.mjs +14 -0
- package/dist/natural/parse.d.mts +99 -0
- package/dist/natural/parse.mjs +66 -0
- package/dist/natural/policy.mjs +6 -0
- package/dist/natural/suggest.d.mts +53 -0
- package/dist/natural/suggest.mjs +24 -0
- package/dist/natural/suggestion.d.mts +4 -0
- package/dist/natural/suggestion.mjs +79 -0
- package/dist/natural/text.d.mts +5 -0
- package/dist/natural/text.mjs +5 -0
- package/dist/resolve/resolve.d.mts +79 -0
- package/dist/resolve/resolve.mjs +66 -0
- package/dist/resolve/schema.d.mts +59 -0
- package/dist/resolve/schema.mjs +28 -0
- package/package.json +15 -3
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { LanguageConflictError, LanguageRegistrationError } from "../language/errors.mjs";
|
|
2
|
+
import { BaseLanguageContribution, LanguagePlugin } from "../language/model.mjs";
|
|
3
|
+
import { LanguageRegistry } from "../language/registry.mjs";
|
|
4
|
+
import "../index.mjs";
|
|
5
|
+
//#region src/locales/en.d.ts
|
|
6
|
+
declare const EnglishContribution: BaseLanguageContribution;
|
|
7
|
+
declare const EnglishLanguage: LanguagePlugin;
|
|
8
|
+
declare const EnglishLanguageLayer: import("effect/Layer").Layer<LanguageRegistry, LanguageConflictError | LanguageRegistrationError, never>;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { EnglishContribution, EnglishLanguage, EnglishLanguageLayer };
|
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import { isIsoDate } from "../ast/schemas.mjs";
|
|
2
|
+
import { BaseLanguageContribution } from "../language/model.mjs";
|
|
3
|
+
import { normalizeNaturalText } from "../natural/text.mjs";
|
|
4
|
+
import { defineLanguagePlugin, languagePluginsLayer } from "../language/registry.mjs";
|
|
5
|
+
import { correctWhitespaceSeparatedText } from "../natural/correction.mjs";
|
|
6
|
+
import { completeNaturalPhrases, fixedCalendarPeriodPhrases, naturalCount, prefixNaturalPhrases } from "../natural/suggestion.mjs";
|
|
7
|
+
import { calendarPeriodOffset, candidate, datedPeriods, datedQuarterPeriods, fixedDatePeriod, fixedMonthPeriod, fixedQuarterPeriod, fixedYearPeriod, fromNowRange, futurePeriod, futureRange, joinedNowCandidate, joinedPeriodCandidate, monthOfRelativeYear, namedDatePeriod, openBoundaryCandidate, parseTrailingCount, periodEndDay, periodRange, periodStartDay, periodToDateRange, periodsFromPhrases, quarterOfRelativeYear, relativePeriod, relativeWeekend, remainingPeriodRange, renderPeriodRange, textAt, trailingPeriod, trailingRange, untilNowRange, validYear } from "./shared.mjs";
|
|
8
|
+
import { Effect, Option, String as String$1 } from "effect";
|
|
9
|
+
//#region src/locales/en.ts
|
|
10
|
+
const months = [
|
|
11
|
+
"january",
|
|
12
|
+
"february",
|
|
13
|
+
"march",
|
|
14
|
+
"april",
|
|
15
|
+
"may",
|
|
16
|
+
"june",
|
|
17
|
+
"july",
|
|
18
|
+
"august",
|
|
19
|
+
"september",
|
|
20
|
+
"october",
|
|
21
|
+
"november",
|
|
22
|
+
"december"
|
|
23
|
+
];
|
|
24
|
+
const monthAbbreviations = [
|
|
25
|
+
["jan"],
|
|
26
|
+
["feb"],
|
|
27
|
+
["mar"],
|
|
28
|
+
["apr"],
|
|
29
|
+
["may"],
|
|
30
|
+
["jun"],
|
|
31
|
+
["jul"],
|
|
32
|
+
["aug"],
|
|
33
|
+
["sep", "sept"],
|
|
34
|
+
["oct"],
|
|
35
|
+
["nov"],
|
|
36
|
+
["dec"]
|
|
37
|
+
];
|
|
38
|
+
const quarterNames = [
|
|
39
|
+
"first",
|
|
40
|
+
"second",
|
|
41
|
+
"third",
|
|
42
|
+
"fourth"
|
|
43
|
+
];
|
|
44
|
+
const units = [
|
|
45
|
+
[
|
|
46
|
+
"day",
|
|
47
|
+
"day",
|
|
48
|
+
"days"
|
|
49
|
+
],
|
|
50
|
+
[
|
|
51
|
+
"week",
|
|
52
|
+
"week",
|
|
53
|
+
"weeks"
|
|
54
|
+
],
|
|
55
|
+
[
|
|
56
|
+
"month",
|
|
57
|
+
"month",
|
|
58
|
+
"months"
|
|
59
|
+
],
|
|
60
|
+
[
|
|
61
|
+
"quarter",
|
|
62
|
+
"quarter",
|
|
63
|
+
"quarters"
|
|
64
|
+
],
|
|
65
|
+
[
|
|
66
|
+
"year",
|
|
67
|
+
"year",
|
|
68
|
+
"years"
|
|
69
|
+
]
|
|
70
|
+
];
|
|
71
|
+
const title = (value) => `${value.slice(0, 1).toLocaleUpperCase("en")}${value.slice(1)}`;
|
|
72
|
+
const currentPeriod = (unit) => unit === "day" ? "today" : `this ${unit}`;
|
|
73
|
+
const remainingPeriodPhrases = units.flatMap((entry) => [
|
|
74
|
+
`rest of the ${entry[0]}`,
|
|
75
|
+
`rest of ${entry[0]}`,
|
|
76
|
+
`rest of ${currentPeriod(entry[0])}`,
|
|
77
|
+
`remainder of the ${entry[0]}`,
|
|
78
|
+
`remaining ${entry[0]}`
|
|
79
|
+
].map((phrase) => ({
|
|
80
|
+
entry,
|
|
81
|
+
phrase
|
|
82
|
+
})));
|
|
83
|
+
const toDateAbbreviations = [
|
|
84
|
+
"dtd",
|
|
85
|
+
"wtd",
|
|
86
|
+
"mtd",
|
|
87
|
+
"qtd",
|
|
88
|
+
"ytd"
|
|
89
|
+
];
|
|
90
|
+
const toDatePhrases = units.flatMap((entry, index) => [
|
|
91
|
+
`${entry[0]} to date`,
|
|
92
|
+
`${entry[0]}-to-date`,
|
|
93
|
+
toDateAbbreviations[index] ?? "",
|
|
94
|
+
`since the start of the ${entry[0]}`,
|
|
95
|
+
`since the beginning of the ${entry[0]}`,
|
|
96
|
+
`from the start of the ${entry[0]} to now`,
|
|
97
|
+
`from the beginning of the ${entry[0]} to now`,
|
|
98
|
+
`${currentPeriod(entry[0])} so far`,
|
|
99
|
+
`so far ${currentPeriod(entry[0])}`,
|
|
100
|
+
`${currentPeriod(entry[0])} until now`,
|
|
101
|
+
`${currentPeriod(entry[0])} up to now`
|
|
102
|
+
].map((phrase) => ({
|
|
103
|
+
entry,
|
|
104
|
+
phrase
|
|
105
|
+
})));
|
|
106
|
+
const monthNumber = (value) => {
|
|
107
|
+
const normalized = value.endsWith(".") ? value.slice(0, -1) : value;
|
|
108
|
+
const fullIndex = months.findIndex((month) => month === normalized);
|
|
109
|
+
if (fullIndex !== -1) return fullIndex + 1;
|
|
110
|
+
const shortIndex = monthAbbreviations.findIndex((aliases) => aliases.some((alias) => alias === normalized));
|
|
111
|
+
return shortIndex === -1 ? void 0 : shortIndex + 1;
|
|
112
|
+
};
|
|
113
|
+
const relativeDirection = (value) => {
|
|
114
|
+
if (value === "last" || value === "previous") return -1;
|
|
115
|
+
if (value === "next" || value === "coming" || value === "upcoming") return 1;
|
|
116
|
+
return 0;
|
|
117
|
+
};
|
|
118
|
+
const relativeDirectionName = (direction) => {
|
|
119
|
+
if (direction < 0) return "last";
|
|
120
|
+
if (direction > 0) return "next";
|
|
121
|
+
return "this";
|
|
122
|
+
};
|
|
123
|
+
const quarterNumber = (value) => {
|
|
124
|
+
if (value.startsWith("q")) return Number(value.slice(1));
|
|
125
|
+
const index = quarterNames.findIndex((name) => name === value);
|
|
126
|
+
return index === -1 ? void 0 : index + 1;
|
|
127
|
+
};
|
|
128
|
+
const parseQuarter = (input) => {
|
|
129
|
+
const fixed = String$1.match(/^(q[1-4])(?: of)? (\d{4})$/u)(input);
|
|
130
|
+
const reversed = String$1.match(/^(\d{4}) (q[1-4])$/u)(input);
|
|
131
|
+
const namedFixed = String$1.match(/^(first|second|third|fourth) quarter(?: of)? (\d{4})$/u)(input);
|
|
132
|
+
const relative = String$1.match(/^(q[1-4])(?: of)? (last|this|next) year$/u)(input);
|
|
133
|
+
const namedRelative = String$1.match(/^(first|second|third|fourth) quarter(?: of)? (last|this|next) year$/u)(input);
|
|
134
|
+
const standalone = String$1.match(/^(q[1-4])$/u)(input);
|
|
135
|
+
const namedStandalone = String$1.match(/^(first|second|third|fourth) quarter$/u)(input);
|
|
136
|
+
const fixedMatch = Option.firstSomeOf([
|
|
137
|
+
fixed,
|
|
138
|
+
reversed,
|
|
139
|
+
namedFixed
|
|
140
|
+
]);
|
|
141
|
+
if (Option.isSome(fixedMatch)) {
|
|
142
|
+
const quarterText = textAt(fixedMatch.value, Option.isSome(reversed) ? 2 : 1);
|
|
143
|
+
const yearText = textAt(fixedMatch.value, Option.isSome(reversed) ? 1 : 2);
|
|
144
|
+
const quarter = quarterNumber(quarterText);
|
|
145
|
+
const year = validYear(yearText);
|
|
146
|
+
if (quarter !== void 0 && year !== void 0) return Option.some(fixedQuarterPeriod(year, quarter, `Q${quarter} ${year}`));
|
|
147
|
+
}
|
|
148
|
+
const relativeMatch = Option.firstSomeOf([relative, namedRelative]);
|
|
149
|
+
if (Option.isSome(relativeMatch)) {
|
|
150
|
+
const directionText = textAt(relativeMatch.value, 2);
|
|
151
|
+
const quarter = quarterNumber(textAt(relativeMatch.value, 1));
|
|
152
|
+
if (quarter !== void 0) {
|
|
153
|
+
const direction = relativeDirection(directionText);
|
|
154
|
+
return Option.some(quarterOfRelativeYear(quarter, direction, `Q${quarter} of ${directionText} year`));
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const standaloneMatch = Option.firstSomeOf([standalone, namedStandalone]);
|
|
158
|
+
if (Option.isNone(standaloneMatch)) return Option.none();
|
|
159
|
+
const quarter = quarterNumber(textAt(standaloneMatch.value, 1));
|
|
160
|
+
return quarter === void 0 ? Option.none() : Option.some(quarterOfRelativeYear(quarter, 0, `Q${quarter}`));
|
|
161
|
+
};
|
|
162
|
+
const parseNamedDate = (input) => {
|
|
163
|
+
const dayFirst = String$1.match(/^([0-3]?\d)(?:st|nd|rd|th)? ([a-z]+\.?) (\d{4})$/u)(input);
|
|
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);
|
|
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
|
+
return Option.none();
|
|
168
|
+
};
|
|
169
|
+
const parseBasePeriod = (input) => {
|
|
170
|
+
if (isIsoDate(input) && input !== "9999-12-31") return Option.some(fixedDatePeriod(input, input));
|
|
171
|
+
const namedDate = parseNamedDate(input);
|
|
172
|
+
if (Option.isSome(namedDate)) return namedDate;
|
|
173
|
+
const quarter = parseQuarter(input);
|
|
174
|
+
if (Option.isSome(quarter)) return quarter;
|
|
175
|
+
const yearMatch = String$1.match(/^(?:(?:the )?(?:calendar )?year )?(\d{4})$/u)(input);
|
|
176
|
+
if (Option.isSome(yearMatch)) {
|
|
177
|
+
const year = validYear(textAt(yearMatch.value, 1));
|
|
178
|
+
if (year !== void 0) return Option.some(fixedYearPeriod(year, String(year)));
|
|
179
|
+
}
|
|
180
|
+
const monthYear = String$1.match(/^([a-z]+\.?) (\d{4})$/u)(input);
|
|
181
|
+
if (Option.isSome(monthYear)) {
|
|
182
|
+
const month = monthNumber(textAt(monthYear.value, 1));
|
|
183
|
+
const year = validYear(textAt(monthYear.value, 2));
|
|
184
|
+
if (month !== void 0 && year !== void 0) return Option.some(fixedMonthPeriod(year, month, `${title(textAt(months, month - 1))} ${year}`));
|
|
185
|
+
}
|
|
186
|
+
const relativeMonth = String$1.match(/^([a-z]+\.?) (?:of )?(last|this|next) year$/u)(input);
|
|
187
|
+
if (Option.isSome(relativeMonth)) {
|
|
188
|
+
const directionText = textAt(relativeMonth.value, 2);
|
|
189
|
+
const month = monthNumber(textAt(relativeMonth.value, 1));
|
|
190
|
+
const direction = relativeDirection(directionText);
|
|
191
|
+
if (month !== void 0) return Option.some(monthOfRelativeYear(month, direction, `${title(textAt(months, month - 1))} of ${directionText} year`));
|
|
192
|
+
}
|
|
193
|
+
const standaloneMonth = monthNumber(input);
|
|
194
|
+
if (standaloneMonth !== void 0) return Option.some(monthOfRelativeYear(standaloneMonth, 0, title(textAt(months, standaloneMonth - 1))));
|
|
195
|
+
if (input === "today") return Option.some(relativePeriod("day", 0, "today"));
|
|
196
|
+
if (input === "yesterday") return Option.some(relativePeriod("day", -1, "yesterday"));
|
|
197
|
+
if (input === "tomorrow") return Option.some(relativePeriod("day", 1, "tomorrow"));
|
|
198
|
+
if (input === "weekend" || input === "the weekend" || input === "this weekend") return Option.some(relativeWeekend(0, "this weekend"));
|
|
199
|
+
if (input === "last weekend" || input === "previous weekend") return Option.some(relativeWeekend(-1, "last weekend"));
|
|
200
|
+
if (input === "next weekend" || input === "coming weekend") return Option.some(relativeWeekend(1, "next weekend"));
|
|
201
|
+
if (input === "the weekend before last") return Option.some(relativeWeekend(-2, "the weekend before last"));
|
|
202
|
+
if (input === "the weekend after next") return Option.some(relativeWeekend(2, "the weekend after next"));
|
|
203
|
+
const articlePeriod = String$1.match(/^the (day|week|month|quarter|year)$/u)(input);
|
|
204
|
+
if (Option.isSome(articlePeriod)) {
|
|
205
|
+
const entry = units.find((unit) => unit[0] === textAt(articlePeriod.value, 1));
|
|
206
|
+
if (entry !== void 0) return Option.some(relativePeriod(entry[1], 0, currentPeriod(entry[0])));
|
|
207
|
+
}
|
|
208
|
+
const outerRelative = String$1.match(/^(?:the )?(day|week|month|quarter|year) (before last|after next)$/u)(input);
|
|
209
|
+
if (Option.isSome(outerRelative)) {
|
|
210
|
+
const entry = units.find((unit) => unit[0] === textAt(outerRelative.value, 1));
|
|
211
|
+
if (entry !== void 0) {
|
|
212
|
+
const direction = textAt(outerRelative.value, 2) === "before last" ? -2 : 2;
|
|
213
|
+
return Option.some(relativePeriod(entry[1], direction, input));
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const relative = String$1.match(/^(?:the )?(last|previous|this|current|next|coming|upcoming) ([a-z]+)$/u)(input);
|
|
217
|
+
if (Option.isSome(relative)) {
|
|
218
|
+
const unitText = textAt(relative.value, 2);
|
|
219
|
+
const unit = units.find((entry) => entry[0] === unitText)?.[1];
|
|
220
|
+
if (unit !== void 0) {
|
|
221
|
+
const direction = relativeDirection(textAt(relative.value, 1));
|
|
222
|
+
const canonicalDirection = relativeDirectionName(direction);
|
|
223
|
+
return Option.some(relativePeriod(unit, direction, `${canonicalDirection} ${unitText}`));
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return Option.none();
|
|
227
|
+
};
|
|
228
|
+
const parsePeriod = (input) => {
|
|
229
|
+
const edge = String$1.match(/^(?:the )?(start|beginning|end) of (.+)$/u)(input);
|
|
230
|
+
if (Option.isSome(edge)) {
|
|
231
|
+
const period = parseBasePeriod(textAt(edge.value, 2));
|
|
232
|
+
if (Option.isSome(period)) {
|
|
233
|
+
const edgeName = textAt(edge.value, 1);
|
|
234
|
+
const name = edgeName === "beginning" ? "start" : edgeName;
|
|
235
|
+
const canonical = `${name} of ${period.value.canonical}`;
|
|
236
|
+
return Option.some(name === "end" ? periodEndDay(period.value, canonical) : periodStartDay(period.value, canonical));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const wrapper = [
|
|
240
|
+
"during ",
|
|
241
|
+
"in ",
|
|
242
|
+
"for ",
|
|
243
|
+
"all of ",
|
|
244
|
+
"the whole of "
|
|
245
|
+
].find((prefix) => input.startsWith(prefix));
|
|
246
|
+
return parseBasePeriod(wrapper === void 0 ? input : input.slice(wrapper.length));
|
|
247
|
+
};
|
|
248
|
+
const boundaryCandidate = (input) => openBoundaryCandidate(input, [
|
|
249
|
+
["up to and including ", "through"],
|
|
250
|
+
["up to including ", "through"],
|
|
251
|
+
["since the beginning of ", "since"],
|
|
252
|
+
["since the start of ", "since"],
|
|
253
|
+
["from the beginning of ", "since"],
|
|
254
|
+
["from the start of ", "since"],
|
|
255
|
+
["starting from ", "since"],
|
|
256
|
+
["through the end of ", "through"],
|
|
257
|
+
["until the end of ", "through"],
|
|
258
|
+
["after the end of ", "after"],
|
|
259
|
+
["until the start of ", "before"],
|
|
260
|
+
["before the beginning of ", "before"],
|
|
261
|
+
["before the start of ", "before"],
|
|
262
|
+
["before beginning of ", "before"],
|
|
263
|
+
["before start of ", "before"],
|
|
264
|
+
["starting ", "since"],
|
|
265
|
+
["through ", "through"],
|
|
266
|
+
["before ", "before"],
|
|
267
|
+
["since ", "since"],
|
|
268
|
+
["after ", "after"],
|
|
269
|
+
["until ", "before"],
|
|
270
|
+
["till ", "before"],
|
|
271
|
+
["up to ", "before"],
|
|
272
|
+
["from ", "since"]
|
|
273
|
+
], parsePeriod);
|
|
274
|
+
const countedUnit = (value, amount) => units.find((entry) => value === (amount === 1 ? entry[0] : entry[2]));
|
|
275
|
+
const parseCalendarOffset = (input) => {
|
|
276
|
+
const singular = String$1.match(/^(?:(?:a|one) (day|week|month|quarter|year) (ago|prior|from now)|in (?:a|one) (day|week|month|quarter|year))$/u)(input);
|
|
277
|
+
if (Option.isSome(singular)) {
|
|
278
|
+
const unitText = textAt(singular.value, 1) || textAt(singular.value, 3);
|
|
279
|
+
const entry = units.find((unit) => unit[0] === unitText);
|
|
280
|
+
if (entry !== void 0) {
|
|
281
|
+
const directionText = textAt(singular.value, 2);
|
|
282
|
+
const isPast = directionText === "ago" || directionText === "prior";
|
|
283
|
+
const direction = isPast ? -1 : 1;
|
|
284
|
+
const canonical = isPast ? `1 ${entry[0]} ago` : `in 1 ${entry[0]}`;
|
|
285
|
+
return Option.some(candidate(periodRange(relativePeriod(entry[1], direction, canonical)), canonical));
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const past = String$1.match(/^([1-9]\d*) (day|days|week|weeks|month|months|quarter|quarters|year|years) (?:ago|prior)$/u)(input);
|
|
289
|
+
const futureIn = String$1.match(/^in ([1-9]\d*) (day|days|week|weeks|month|months|quarter|quarters|year|years)$/u)(input);
|
|
290
|
+
const futureFromNow = String$1.match(/^([1-9]\d*) (day|days|week|weeks|month|months|quarter|quarters|year|years) from now$/u)(input);
|
|
291
|
+
const match = Option.firstSomeOf([
|
|
292
|
+
past,
|
|
293
|
+
futureIn,
|
|
294
|
+
futureFromNow
|
|
295
|
+
]);
|
|
296
|
+
if (Option.isNone(match)) return Option.none();
|
|
297
|
+
const amount = parseTrailingCount(textAt(match.value, 1));
|
|
298
|
+
if (Option.isNone(amount)) return Option.none();
|
|
299
|
+
const unitText = textAt(match.value, 2);
|
|
300
|
+
const entry = countedUnit(unitText, amount.value);
|
|
301
|
+
if (entry === void 0) return Option.none();
|
|
302
|
+
const direction = Option.isSome(past) ? -amount.value : amount.value;
|
|
303
|
+
const canonical = direction < 0 ? `${amount.value} ${unitText} ago` : `in ${amount.value} ${unitText}`;
|
|
304
|
+
return Option.some(candidate(periodRange(relativePeriod(entry[1], direction, canonical)), canonical));
|
|
305
|
+
};
|
|
306
|
+
const parseRollingPeriod = (input) => {
|
|
307
|
+
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);
|
|
308
|
+
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 match = Option.firstSomeOf([past, future]);
|
|
310
|
+
if (Option.isNone(match)) return Option.none();
|
|
311
|
+
const amount = parseTrailingCount(textAt(match.value, 1));
|
|
312
|
+
if (Option.isNone(amount)) return Option.none();
|
|
313
|
+
const entry = countedUnit(textAt(match.value, 2), amount.value);
|
|
314
|
+
if (entry === void 0) return Option.none();
|
|
315
|
+
const isFuture = Option.isSome(future);
|
|
316
|
+
const range = isFuture ? futureRange(amount.value, entry[1]) : trailingRange(amount.value, entry[1]);
|
|
317
|
+
const direction = isFuture ? "next" : "last";
|
|
318
|
+
const noun = amount.value === 1 ? entry[0] : entry[2];
|
|
319
|
+
return Option.some(candidate(range, `${direction} ${amount.value} ${noun}`));
|
|
320
|
+
};
|
|
321
|
+
const parseEnglish = (input) => {
|
|
322
|
+
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]}`));
|
|
324
|
+
if ([
|
|
325
|
+
"until now",
|
|
326
|
+
"till now",
|
|
327
|
+
"up to now",
|
|
328
|
+
"through now"
|
|
329
|
+
].includes(input)) return Option.some(candidate(untilNowRange(), "until now"));
|
|
330
|
+
if ([
|
|
331
|
+
"from now",
|
|
332
|
+
"from now on",
|
|
333
|
+
"starting now"
|
|
334
|
+
].includes(input)) return Option.some(candidate(fromNowRange(), "from now"));
|
|
335
|
+
const offset = parseCalendarOffset(input);
|
|
336
|
+
if (Option.isSome(offset)) return offset;
|
|
337
|
+
const rolling = parseRollingPeriod(input);
|
|
338
|
+
if (Option.isSome(rolling)) return rolling;
|
|
339
|
+
const toDate = toDatePhrases.find((entry) => entry.phrase === input);
|
|
340
|
+
if (toDate !== void 0) return Option.some(candidate(periodToDateRange(toDate.entry[1]), `${toDate.entry[0]} to date`));
|
|
341
|
+
const nowBounded = joinedNowCandidate(input, [
|
|
342
|
+
["from ", " to now"],
|
|
343
|
+
["from ", " until now"],
|
|
344
|
+
["from ", " through now"],
|
|
345
|
+
["between ", " and now"]
|
|
346
|
+
], [
|
|
347
|
+
"from now to ",
|
|
348
|
+
"from now until ",
|
|
349
|
+
"from now through ",
|
|
350
|
+
"between now and "
|
|
351
|
+
], parsePeriod, (period) => `from ${period} to now`, (period) => `from now to ${period}`);
|
|
352
|
+
if (Option.isSome(nowBounded)) return nowBounded;
|
|
353
|
+
const bounded = joinedPeriodCandidate(input, [
|
|
354
|
+
["from ", " to and including "],
|
|
355
|
+
["from ", " through and including "],
|
|
356
|
+
["from ", " to "],
|
|
357
|
+
["from ", " until "],
|
|
358
|
+
["from ", " through "],
|
|
359
|
+
["from ", " till "],
|
|
360
|
+
["between ", " and "],
|
|
361
|
+
["between ", " through "],
|
|
362
|
+
["", " to and including "],
|
|
363
|
+
["", " through and including "],
|
|
364
|
+
["", " to "],
|
|
365
|
+
["", " until "],
|
|
366
|
+
["", " through "],
|
|
367
|
+
["", " till "],
|
|
368
|
+
["", " - "],
|
|
369
|
+
["", " – "],
|
|
370
|
+
["", " — "],
|
|
371
|
+
["between ", "-"],
|
|
372
|
+
["", "-"],
|
|
373
|
+
["between ", "–"],
|
|
374
|
+
["", "–"],
|
|
375
|
+
["between ", "—"],
|
|
376
|
+
["", "—"],
|
|
377
|
+
["between ", "~"],
|
|
378
|
+
["", "~"]
|
|
379
|
+
], parsePeriod, (lower, upper) => `from ${lower} to ${upper}`);
|
|
380
|
+
if (Option.isSome(bounded)) return bounded;
|
|
381
|
+
const boundary = boundaryCandidate(input);
|
|
382
|
+
if (Option.isSome(boundary)) return boundary;
|
|
383
|
+
return Option.map(parsePeriod(input), (period) => candidate(periodRange(period), period.canonical));
|
|
384
|
+
};
|
|
385
|
+
const staticPeriodPhrases = [
|
|
386
|
+
"today",
|
|
387
|
+
"yesterday",
|
|
388
|
+
"tomorrow",
|
|
389
|
+
"this weekend",
|
|
390
|
+
"last weekend",
|
|
391
|
+
"next weekend",
|
|
392
|
+
"the weekend before last",
|
|
393
|
+
"the weekend after next",
|
|
394
|
+
...units.flatMap((entry) => [
|
|
395
|
+
`this ${entry[0]}`,
|
|
396
|
+
`last ${entry[0]}`,
|
|
397
|
+
`next ${entry[0]}`,
|
|
398
|
+
`start of this ${entry[0]}`,
|
|
399
|
+
`end of this ${entry[0]}`,
|
|
400
|
+
`start of last ${entry[0]}`,
|
|
401
|
+
`end of last ${entry[0]}`,
|
|
402
|
+
`start of next ${entry[0]}`,
|
|
403
|
+
`end of next ${entry[0]}`
|
|
404
|
+
]),
|
|
405
|
+
...[
|
|
406
|
+
1,
|
|
407
|
+
2,
|
|
408
|
+
3,
|
|
409
|
+
4
|
|
410
|
+
].flatMap((quarter) => [
|
|
411
|
+
`q${quarter}`,
|
|
412
|
+
`q${quarter} of last year`,
|
|
413
|
+
`q${quarter} of next year`
|
|
414
|
+
]),
|
|
415
|
+
...months.flatMap((month) => [
|
|
416
|
+
month,
|
|
417
|
+
`${month} of last year`,
|
|
418
|
+
`${month} of next year`
|
|
419
|
+
])
|
|
420
|
+
];
|
|
421
|
+
const staticPeriods = periodsFromPhrases(staticPeriodPhrases, parsePeriod);
|
|
422
|
+
const boundaryPrefixes = [
|
|
423
|
+
"since ",
|
|
424
|
+
"before ",
|
|
425
|
+
"through ",
|
|
426
|
+
"after "
|
|
427
|
+
];
|
|
428
|
+
const countedSuggestions = (input) => {
|
|
429
|
+
const amount = naturalCount(input);
|
|
430
|
+
if (amount === void 0) return [];
|
|
431
|
+
return units.flatMap((entry) => {
|
|
432
|
+
const noun = amount === 1 ? entry[0] : entry[2];
|
|
433
|
+
return [
|
|
434
|
+
`last ${amount} ${noun}`,
|
|
435
|
+
`${amount} ${noun}`,
|
|
436
|
+
`next ${amount} ${noun}`,
|
|
437
|
+
`${amount} ${noun} ago`,
|
|
438
|
+
`in ${amount} ${noun}`
|
|
439
|
+
];
|
|
440
|
+
});
|
|
441
|
+
};
|
|
442
|
+
const englishSuggestionPhrases = [
|
|
443
|
+
...units.map((entry) => `${entry[0]} to date`),
|
|
444
|
+
...units.map((entry) => `rest of ${entry[0]}`),
|
|
445
|
+
...staticPeriodPhrases,
|
|
446
|
+
...prefixNaturalPhrases(staticPeriodPhrases, boundaryPrefixes),
|
|
447
|
+
"until now",
|
|
448
|
+
"from now"
|
|
449
|
+
];
|
|
450
|
+
const suggestEnglish = (input, limit) => {
|
|
451
|
+
const fixed = fixedCalendarPeriodPhrases(input, months);
|
|
452
|
+
return completeNaturalPhrases(input, [
|
|
453
|
+
...englishSuggestionPhrases,
|
|
454
|
+
...fixed,
|
|
455
|
+
...prefixNaturalPhrases(fixed, boundaryPrefixes),
|
|
456
|
+
...countedSuggestions(input)
|
|
457
|
+
], limit);
|
|
458
|
+
};
|
|
459
|
+
const renderEnglish = (range) => {
|
|
460
|
+
const offset = calendarPeriodOffset(range);
|
|
461
|
+
if (Option.isSome(offset) && Math.abs(offset.value.amount) > 1) {
|
|
462
|
+
const entry = units.find((unit) => unit[1] === offset.value.unit);
|
|
463
|
+
if (entry !== void 0) {
|
|
464
|
+
const noun = offset.value.amount === -1 || offset.value.amount === 1 ? entry[0] : entry[2];
|
|
465
|
+
return Option.some(offset.value.amount < 0 ? `${-offset.value.amount} ${noun} ago` : `in ${offset.value.amount} ${noun}`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
const future = futurePeriod(range);
|
|
469
|
+
if (Option.isSome(future)) {
|
|
470
|
+
const entry = units.find((unit) => unit[1] === future.value.unit);
|
|
471
|
+
if (entry !== void 0) return Option.some(`next ${future.value.amount} ${future.value.amount === 1 ? entry[0] : entry[2]}`);
|
|
472
|
+
}
|
|
473
|
+
const trailing = trailingPeriod(range);
|
|
474
|
+
if (Option.isSome(trailing)) {
|
|
475
|
+
const entry = units.find((unit) => unit[1] === trailing.value.unit);
|
|
476
|
+
if (entry !== void 0) return Option.some(trailing.value.amount === 1 ? `1 ${entry[0]}` : `last ${trailing.value.amount} ${entry[2]}`);
|
|
477
|
+
}
|
|
478
|
+
const periods = [...staticPeriods, ...periodsFromPhrases([...datedPeriods(range, months), ...datedQuarterPeriods(range)], parsePeriod)];
|
|
479
|
+
return renderPeriodRange(range, [...units.map((entry) => candidate(periodToDateRange(entry[1]), `${entry[0]} to date`)), ...units.map((entry) => candidate(remainingPeriodRange(entry[1]), `rest of ${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
|
+
};
|
|
481
|
+
const EnglishContribution = new BaseLanguageContribution({
|
|
482
|
+
locale: "en",
|
|
483
|
+
vocabulary: [
|
|
484
|
+
...months,
|
|
485
|
+
...quarterNames,
|
|
486
|
+
"q1",
|
|
487
|
+
"q2",
|
|
488
|
+
"q3",
|
|
489
|
+
"q4",
|
|
490
|
+
...monthAbbreviations.flatMap((aliases) => aliases),
|
|
491
|
+
...toDatePhrases.flatMap((entry) => entry.phrase.split(" ")),
|
|
492
|
+
...remainingPeriodPhrases.flatMap((entry) => entry.phrase.split(" ")),
|
|
493
|
+
...units.flatMap((entry) => [entry[0], entry[2]]),
|
|
494
|
+
"after",
|
|
495
|
+
"ago",
|
|
496
|
+
"all",
|
|
497
|
+
"one",
|
|
498
|
+
"and",
|
|
499
|
+
"beginning",
|
|
500
|
+
"before",
|
|
501
|
+
"calendar",
|
|
502
|
+
"coming",
|
|
503
|
+
"current",
|
|
504
|
+
"date",
|
|
505
|
+
"during",
|
|
506
|
+
"end",
|
|
507
|
+
"far",
|
|
508
|
+
"for",
|
|
509
|
+
"from",
|
|
510
|
+
"in",
|
|
511
|
+
"including",
|
|
512
|
+
"last",
|
|
513
|
+
"next",
|
|
514
|
+
"now",
|
|
515
|
+
"on",
|
|
516
|
+
"over",
|
|
517
|
+
"of",
|
|
518
|
+
"past",
|
|
519
|
+
"previous",
|
|
520
|
+
"prior",
|
|
521
|
+
"since",
|
|
522
|
+
"so",
|
|
523
|
+
"start",
|
|
524
|
+
"starting",
|
|
525
|
+
"this",
|
|
526
|
+
"through",
|
|
527
|
+
"till",
|
|
528
|
+
"whole",
|
|
529
|
+
"to",
|
|
530
|
+
"until",
|
|
531
|
+
"upcoming",
|
|
532
|
+
"within",
|
|
533
|
+
"today",
|
|
534
|
+
"tomorrow",
|
|
535
|
+
"weekend",
|
|
536
|
+
"yesterday"
|
|
537
|
+
],
|
|
538
|
+
normalize: normalizeNaturalText,
|
|
539
|
+
correct: correctWhitespaceSeparatedText,
|
|
540
|
+
parseExact: parseEnglish,
|
|
541
|
+
suggest: suggestEnglish,
|
|
542
|
+
render: renderEnglish
|
|
543
|
+
});
|
|
544
|
+
const EnglishLanguage = defineLanguagePlugin({
|
|
545
|
+
id: "chronolizer/language-en",
|
|
546
|
+
effect: (context) => Effect.asVoid(context.register("chronolizer/language-en", EnglishContribution))
|
|
547
|
+
});
|
|
548
|
+
const EnglishLanguageLayer = languagePluginsLayer([EnglishLanguage]);
|
|
549
|
+
//#endregion
|
|
550
|
+
export { EnglishContribution, EnglishLanguage, EnglishLanguageLayer };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { LanguageConflictError, LanguageRegistrationError } from "../language/errors.mjs";
|
|
2
|
+
import { BaseLanguageContribution, LanguagePlugin } from "../language/model.mjs";
|
|
3
|
+
import { LanguageRegistry } from "../language/registry.mjs";
|
|
4
|
+
import "../index.mjs";
|
|
5
|
+
//#region src/locales/es.d.ts
|
|
6
|
+
declare const SpanishContribution: BaseLanguageContribution;
|
|
7
|
+
declare const SpanishLanguage: LanguagePlugin;
|
|
8
|
+
declare const SpanishLanguageLayer: import("effect/Layer").Layer<LanguageRegistry, LanguageConflictError | LanguageRegistrationError, never>;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { SpanishContribution, SpanishLanguage, SpanishLanguageLayer };
|