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.
Files changed (62) hide show
  1. package/README.md +70 -7
  2. package/dist/ast/constructors.d.mts +140 -0
  3. package/dist/ast/constructors.mjs +25 -0
  4. package/dist/ast/fold.d.mts +12 -0
  5. package/dist/ast/fold.mjs +51 -0
  6. package/dist/ast/normalize.d.mts +42 -0
  7. package/dist/ast/normalize.mjs +32 -0
  8. package/dist/ast/schemas.d.mts +87 -0
  9. package/dist/ast/schemas.mjs +74 -0
  10. package/dist/filter/codec.d.mts +92 -0
  11. package/dist/filter/codec.mjs +48 -0
  12. package/dist/filter/errors.d.mts +14 -0
  13. package/dist/filter/errors.mjs +10 -0
  14. package/dist/filter/expression.d.mts +8 -0
  15. package/dist/filter/expression.mjs +93 -0
  16. package/dist/filter/schema.d.mts +13 -0
  17. package/dist/filter/schema.mjs +11 -0
  18. package/dist/filter/transformation.d.mts +37 -0
  19. package/dist/filter/transformation.mjs +20 -0
  20. package/dist/index.d.mts +22 -969
  21. package/dist/index.mjs +22 -2305
  22. package/dist/language/errors.d.mts +38 -0
  23. package/dist/language/errors.mjs +30 -0
  24. package/dist/language/model.d.mts +239 -0
  25. package/dist/language/model.mjs +53 -0
  26. package/dist/language/registry.d.mts +17 -0
  27. package/dist/language/registry.mjs +134 -0
  28. package/dist/locales/cs.d.mts +10 -0
  29. package/dist/locales/cs.mjs +693 -0
  30. package/dist/locales/de.d.mts +10 -0
  31. package/dist/locales/de.mjs +828 -0
  32. package/dist/locales/en.d.mts +10 -0
  33. package/dist/locales/en.mjs +550 -0
  34. package/dist/locales/es.d.mts +10 -0
  35. package/dist/locales/es.mjs +660 -0
  36. package/dist/locales/fr.d.mts +10 -0
  37. package/dist/locales/fr.mjs +737 -0
  38. package/dist/locales/nl.d.mts +10 -0
  39. package/dist/locales/nl.mjs +652 -0
  40. package/dist/locales/pl.d.mts +10 -0
  41. package/dist/locales/pl.mjs +711 -0
  42. package/dist/locales/shared.mjs +289 -0
  43. package/dist/locales/tr.d.mts +10 -0
  44. package/dist/locales/tr.mjs +660 -0
  45. package/dist/natural/correction.d.mts +13 -0
  46. package/dist/natural/correction.mjs +73 -0
  47. package/dist/natural/format.d.mts +47 -0
  48. package/dist/natural/format.mjs +14 -0
  49. package/dist/natural/parse.d.mts +99 -0
  50. package/dist/natural/parse.mjs +66 -0
  51. package/dist/natural/policy.mjs +6 -0
  52. package/dist/natural/suggest.d.mts +53 -0
  53. package/dist/natural/suggest.mjs +24 -0
  54. package/dist/natural/suggestion.d.mts +4 -0
  55. package/dist/natural/suggestion.mjs +79 -0
  56. package/dist/natural/text.d.mts +5 -0
  57. package/dist/natural/text.mjs +5 -0
  58. package/dist/resolve/resolve.d.mts +79 -0
  59. package/dist/resolve/resolve.mjs +66 -0
  60. package/dist/resolve/schema.d.mts +59 -0
  61. package/dist/resolve/schema.mjs +28 -0
  62. package/package.json +15 -3
@@ -0,0 +1,660 @@
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, isoDate, 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/es.ts
10
+ const months = [
11
+ "enero",
12
+ "febrero",
13
+ "marzo",
14
+ "abril",
15
+ "mayo",
16
+ "junio",
17
+ "julio",
18
+ "agosto",
19
+ "septiembre",
20
+ "octubre",
21
+ "noviembre",
22
+ "diciembre"
23
+ ];
24
+ const monthAbbreviations = [
25
+ ["ene"],
26
+ ["feb"],
27
+ ["mar"],
28
+ ["abr"],
29
+ ["may"],
30
+ ["jun"],
31
+ ["jul"],
32
+ ["ago"],
33
+ [
34
+ "sep",
35
+ "sept",
36
+ "set",
37
+ "setiembre"
38
+ ],
39
+ ["oct"],
40
+ ["nov"],
41
+ ["dic"]
42
+ ];
43
+ const units = [
44
+ {
45
+ unit: "day",
46
+ singular: "día",
47
+ plural: "días",
48
+ current: "hoy",
49
+ previous: "ayer",
50
+ next: "mañana",
51
+ toDate: "día hasta la fecha",
52
+ remaining: "resto del día"
53
+ },
54
+ {
55
+ unit: "week",
56
+ singular: "semana",
57
+ plural: "semanas",
58
+ current: "esta semana",
59
+ previous: "la semana pasada",
60
+ next: "la próxima semana",
61
+ toDate: "semana hasta la fecha",
62
+ remaining: "resto de la semana"
63
+ },
64
+ {
65
+ unit: "month",
66
+ singular: "mes",
67
+ plural: "meses",
68
+ current: "este mes",
69
+ previous: "el mes pasado",
70
+ next: "el próximo mes",
71
+ toDate: "mes hasta la fecha",
72
+ remaining: "resto del mes"
73
+ },
74
+ {
75
+ unit: "quarter",
76
+ singular: "trimestre",
77
+ plural: "trimestres",
78
+ current: "este trimestre",
79
+ previous: "el trimestre pasado",
80
+ next: "el próximo trimestre",
81
+ toDate: "trimestre hasta la fecha",
82
+ remaining: "resto del trimestre"
83
+ },
84
+ {
85
+ unit: "year",
86
+ singular: "año",
87
+ plural: "años",
88
+ current: "este año",
89
+ previous: "el año pasado",
90
+ next: "el próximo año",
91
+ toDate: "año hasta la fecha",
92
+ remaining: "resto del año"
93
+ }
94
+ ];
95
+ const title = (value) => `${value.slice(0, 1).toLocaleUpperCase("es")}${value.slice(1)}`;
96
+ const unitAliases = [
97
+ ["día", "day"],
98
+ ["dias", "day"],
99
+ ["días", "day"],
100
+ ["semana", "week"],
101
+ ["semanas", "week"],
102
+ ["mes", "month"],
103
+ ["meses", "month"],
104
+ ["trimestre", "quarter"],
105
+ ["trimestres", "quarter"],
106
+ ["año", "year"],
107
+ ["ano", "year"],
108
+ ["años", "year"],
109
+ ["anos", "year"]
110
+ ];
111
+ const periodAliases = [
112
+ ...units.flatMap((entry) => [
113
+ [
114
+ entry.current,
115
+ entry.unit,
116
+ 0,
117
+ entry.current
118
+ ],
119
+ [
120
+ entry.previous,
121
+ entry.unit,
122
+ -1,
123
+ entry.previous
124
+ ],
125
+ [
126
+ entry.next,
127
+ entry.unit,
128
+ 1,
129
+ entry.next
130
+ ]
131
+ ]),
132
+ [
133
+ "semana pasada",
134
+ "week",
135
+ -1,
136
+ "la semana pasada"
137
+ ],
138
+ [
139
+ "semana que viene",
140
+ "week",
141
+ 1,
142
+ "la próxima semana"
143
+ ],
144
+ [
145
+ "la semana que viene",
146
+ "week",
147
+ 1,
148
+ "la próxima semana"
149
+ ],
150
+ [
151
+ "proxima semana",
152
+ "week",
153
+ 1,
154
+ "la próxima semana"
155
+ ],
156
+ [
157
+ "el pasado mes",
158
+ "month",
159
+ -1,
160
+ "el mes pasado"
161
+ ],
162
+ [
163
+ "mes pasado",
164
+ "month",
165
+ -1,
166
+ "el mes pasado"
167
+ ],
168
+ [
169
+ "mes que viene",
170
+ "month",
171
+ 1,
172
+ "el próximo mes"
173
+ ],
174
+ [
175
+ "el mes que viene",
176
+ "month",
177
+ 1,
178
+ "el próximo mes"
179
+ ],
180
+ [
181
+ "el proximo mes",
182
+ "month",
183
+ 1,
184
+ "el próximo mes"
185
+ ],
186
+ [
187
+ "año pasado",
188
+ "year",
189
+ -1,
190
+ "el año pasado"
191
+ ],
192
+ [
193
+ "ano pasado",
194
+ "year",
195
+ -1,
196
+ "el año pasado"
197
+ ],
198
+ [
199
+ "año que viene",
200
+ "year",
201
+ 1,
202
+ "el próximo año"
203
+ ],
204
+ [
205
+ "el año que viene",
206
+ "year",
207
+ 1,
208
+ "el próximo año"
209
+ ],
210
+ [
211
+ "el proximo año",
212
+ "year",
213
+ 1,
214
+ "el próximo año"
215
+ ],
216
+ [
217
+ "este ano",
218
+ "year",
219
+ 0,
220
+ "este año"
221
+ ],
222
+ [
223
+ "trimestre pasado",
224
+ "quarter",
225
+ -1,
226
+ "el trimestre pasado"
227
+ ],
228
+ [
229
+ "proximo trimestre",
230
+ "quarter",
231
+ 1,
232
+ "el próximo trimestre"
233
+ ],
234
+ [
235
+ "anteayer",
236
+ "day",
237
+ -2,
238
+ "anteayer"
239
+ ],
240
+ [
241
+ "pasado mañana",
242
+ "day",
243
+ 2,
244
+ "pasado mañana"
245
+ ],
246
+ [
247
+ "la semana anterior a la pasada",
248
+ "week",
249
+ -2,
250
+ "la semana anterior a la pasada"
251
+ ],
252
+ [
253
+ "la semana después de la próxima",
254
+ "week",
255
+ 2,
256
+ "la semana después de la próxima"
257
+ ],
258
+ [
259
+ "el mes anterior al pasado",
260
+ "month",
261
+ -2,
262
+ "el mes anterior al pasado"
263
+ ],
264
+ [
265
+ "el mes después del próximo",
266
+ "month",
267
+ 2,
268
+ "el mes después del próximo"
269
+ ],
270
+ [
271
+ "el trimestre anterior al pasado",
272
+ "quarter",
273
+ -2,
274
+ "el trimestre anterior al pasado"
275
+ ],
276
+ [
277
+ "el trimestre después del próximo",
278
+ "quarter",
279
+ 2,
280
+ "el trimestre después del próximo"
281
+ ],
282
+ [
283
+ "el año anterior al pasado",
284
+ "year",
285
+ -2,
286
+ "el año anterior al pasado"
287
+ ],
288
+ [
289
+ "el año después del próximo",
290
+ "year",
291
+ 2,
292
+ "el año después del próximo"
293
+ ]
294
+ ];
295
+ const toDatePhrases = units.flatMap((entry) => {
296
+ const noun = entry.singular;
297
+ return [
298
+ entry.toDate,
299
+ `en lo que va del ${noun}`,
300
+ `desde el inicio del ${noun}`,
301
+ `desde el comienzo del ${noun}`,
302
+ `desde principios del ${noun}`,
303
+ `${entry.current} hasta ahora`
304
+ ].map((phrase) => ({
305
+ entry,
306
+ phrase
307
+ }));
308
+ });
309
+ const remainingPhrases = units.flatMap((entry) => [entry.remaining, `lo que queda del ${entry.singular}`].map((phrase) => ({
310
+ entry,
311
+ phrase
312
+ })));
313
+ const relativeYearDirection = (value) => {
314
+ if (value.includes("pasado")) return -1;
315
+ if (value.includes("ximo")) return 1;
316
+ return 0;
317
+ };
318
+ const relativeYearName = (direction) => {
319
+ if (direction < 0) return "año pasado";
320
+ if (direction > 0) return "próximo año";
321
+ return "este año";
322
+ };
323
+ const monthNumber = (value) => {
324
+ const normalized = value.endsWith(".") ? value.slice(0, -1) : value;
325
+ const full = months.findIndex((month) => month === normalized);
326
+ if (full !== -1) return full + 1;
327
+ const short = monthAbbreviations.findIndex((aliases) => aliases.some((alias) => alias === normalized));
328
+ return short === -1 ? void 0 : short + 1;
329
+ };
330
+ const parseNamedDate = (input) => {
331
+ const named = String$1.match(/^([0-3]?\d)(?: de)? ([a-záéíóúñ]+\.?)(?: de)? (\d{4})$/u)(input);
332
+ if (Option.isSome(named)) return namedDatePeriod(textAt(named.value, 3), textAt(named.value, 2), textAt(named.value, 1), monthNumber, (day, month, year) => `${day} de ${textAt(months, month - 1)} de ${year}`);
333
+ const numeric = String$1.match(/^([0-3]?\d)[./-]([01]?\d)[./-](\d{4})$/u)(input);
334
+ if (Option.isNone(numeric)) return Option.none();
335
+ const year = validYear(textAt(numeric.value, 3));
336
+ const month = Number(textAt(numeric.value, 2));
337
+ const day = Number(textAt(numeric.value, 1));
338
+ if (year === void 0 || month < 1 || month > 12) return Option.none();
339
+ const value = isoDate(year, month, day);
340
+ return isIsoDate(value) && value !== "9999-12-31" ? Option.some(fixedDatePeriod(value, `${day} de ${textAt(months, month - 1)} de ${year}`)) : Option.none();
341
+ };
342
+ const quarterNumber = (value) => {
343
+ if ((value.startsWith("q") || value.startsWith("t")) && value.length === 2) return Number(value.slice(1));
344
+ if (value === "primer" || value === "primero") return 1;
345
+ if (value === "segundo") return 2;
346
+ if (value === "tercer" || value === "tercero") return 3;
347
+ return value === "cuarto" ? 4 : void 0;
348
+ };
349
+ const parseQuarter = (input) => {
350
+ const fixed = String$1.match(/^(t[1-4]|q[1-4]|primer|primero|segundo|tercer|tercero|cuarto)(?: trimestre)?(?: de)? (\d{4})$/u)(input);
351
+ if (Option.isSome(fixed)) {
352
+ const quarter = quarterNumber(textAt(fixed.value, 1));
353
+ const year = validYear(textAt(fixed.value, 2));
354
+ if (quarter !== void 0 && year !== void 0) return Option.some(fixedQuarterPeriod(year, quarter, `T${quarter} ${year}`));
355
+ }
356
+ const relative = String$1.match(/^(t[1-4]|q[1-4]) (?:del (año pasado|ano pasado|próximo año|proximo año)|de (este año|este ano))$/u)(input);
357
+ if (Option.isSome(relative)) {
358
+ const quarter = quarterNumber(textAt(relative.value, 1));
359
+ const yearText = textAt(relative.value, 2) || textAt(relative.value, 3);
360
+ const direction = relativeYearDirection(yearText);
361
+ if (quarter !== void 0) return Option.some(quarterOfRelativeYear(quarter, direction, `T${quarter} del ${relativeYearName(direction)}`));
362
+ }
363
+ const standalone = String$1.match(/^(?:el )?(t[1-4]|q[1-4])$/u)(input);
364
+ if (Option.isNone(standalone)) return Option.none();
365
+ const quarter = quarterNumber(textAt(standalone.value, 1));
366
+ return quarter === void 0 ? Option.none() : Option.some(quarterOfRelativeYear(quarter, 0, `T${quarter}`));
367
+ };
368
+ const parseBasePeriod = (input) => {
369
+ if (isIsoDate(input) && input !== "9999-12-31") return Option.some(fixedDatePeriod(input, input));
370
+ const namedDate = parseNamedDate(input);
371
+ if (Option.isSome(namedDate)) return namedDate;
372
+ const quarter = parseQuarter(input);
373
+ if (Option.isSome(quarter)) return quarter;
374
+ const yearMatch = String$1.match(/^(?:(?:el )?año )?(\d{4})$/u)(input);
375
+ if (Option.isSome(yearMatch)) {
376
+ const year = validYear(textAt(yearMatch.value, 1));
377
+ if (year !== void 0) return Option.some(fixedYearPeriod(year, String(year)));
378
+ }
379
+ const monthYear = String$1.match(/^([a-záéíóúñ]+\.?)(?: de)? (\d{4})$/u)(input);
380
+ if (Option.isSome(monthYear)) {
381
+ const month = monthNumber(textAt(monthYear.value, 1));
382
+ const year = validYear(textAt(monthYear.value, 2));
383
+ if (month !== void 0 && year !== void 0) return Option.some(fixedMonthPeriod(year, month, `${title(textAt(months, month - 1))} de ${year}`));
384
+ }
385
+ 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);
386
+ if (Option.isSome(relativeMonth)) {
387
+ const month = monthNumber(textAt(relativeMonth.value, 1));
388
+ const yearText = textAt(relativeMonth.value, 2);
389
+ const direction = relativeYearDirection(yearText);
390
+ if (month !== void 0) {
391
+ const canonicalYear = relativeYearName(direction);
392
+ return Option.some(monthOfRelativeYear(month, direction, `${title(textAt(months, month - 1))} del ${canonicalYear}`));
393
+ }
394
+ }
395
+ const standaloneMonth = monthNumber(input);
396
+ if (standaloneMonth !== void 0) return Option.some(monthOfRelativeYear(standaloneMonth, 0, title(textAt(months, standaloneMonth - 1))));
397
+ const alias = periodAliases.find((entry) => entry[0] === input);
398
+ if (alias !== void 0) return Option.some(relativePeriod(alias[1], alias[2], alias[3]));
399
+ if ([
400
+ "fin de semana",
401
+ "el fin de semana",
402
+ "este fin de semana"
403
+ ].includes(input)) return Option.some(relativeWeekend(0, "este fin de semana"));
404
+ if (["el fin de semana pasado", "fin de semana pasado"].includes(input)) return Option.some(relativeWeekend(-1, "el fin de semana pasado"));
405
+ 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"));
406
+ if (input === "el fin de semana anterior al pasado") return Option.some(relativeWeekend(-2, input));
407
+ if (input === "el fin de semana después del próximo") return Option.some(relativeWeekend(2, input));
408
+ return Option.none();
409
+ };
410
+ const parsePeriod = (input) => {
411
+ const edge = String$1.match(/^(?:el )?(inicio|comienzo|principio|fin|final)(?: de| del) (.+)$/u)(input);
412
+ if (Option.isSome(edge)) {
413
+ const edgeName = textAt(edge.value, 1);
414
+ const period = parseBasePeriod(textAt(edge.value, 2));
415
+ if (Option.isSome(period)) {
416
+ const isEnd = edgeName === "fin" || edgeName === "final";
417
+ const canonical = `${isEnd ? "final" : "inicio"} de ${period.value.canonical}`;
418
+ return Option.some(isEnd ? periodEndDay(period.value, canonical) : periodStartDay(period.value, canonical));
419
+ }
420
+ }
421
+ const wrapper = [
422
+ "durante ",
423
+ "en ",
424
+ "todo ",
425
+ "todo el ",
426
+ "toda la "
427
+ ].find((prefix) => input.startsWith(prefix));
428
+ return parseBasePeriod(wrapper === void 0 ? input : input.slice(wrapper.length));
429
+ };
430
+ const countedUnit = (value) => unitAliases.find((entry) => entry[0] === value)?.[1];
431
+ const parseCalendarOffset = (input) => {
432
+ const past = String$1.match(/^hace ([1-9]\d*) (día|días|dias|semana|semanas|mes|meses|trimestre|trimestres|año|años|ano|anos)$/u)(input);
433
+ const future = String$1.match(/^(?:dentro de|en) ([1-9]\d*) (día|días|dias|semana|semanas|mes|meses|trimestre|trimestres|año|años|ano|anos)$/u)(input);
434
+ const match = Option.firstSomeOf([past, future]);
435
+ if (Option.isNone(match)) return Option.none();
436
+ const amount = parseTrailingCount(textAt(match.value, 1));
437
+ const unit = countedUnit(textAt(match.value, 2));
438
+ if (Option.isNone(amount) || unit === void 0) return Option.none();
439
+ const entry = units.find((item) => item.unit === unit);
440
+ if (entry === void 0) return Option.none();
441
+ const direction = Option.isSome(past) ? -amount.value : amount.value;
442
+ const canonical = direction < 0 ? `hace ${amount.value} ${amount.value === 1 ? entry.singular : entry.plural}` : `dentro de ${amount.value} ${amount.value === 1 ? entry.singular : entry.plural}`;
443
+ return Option.some(candidate(periodRange(relativePeriod(unit, direction, canonical)), canonical));
444
+ };
445
+ const parseRollingPeriod = (input) => {
446
+ const sinceAgo = String$1.match(/^desde hace ([1-9]\d*) (día|días|dias|semana|semanas|mes|meses|trimestre|trimestres|año|años|ano|anos)$/u)(input);
447
+ const past = String$1.match(/^(?:(?:los|las) )?(?:últimos|ultimos|últimas|ultimas|pasados|pasadas|anteriores) ([1-9]\d*) (día|días|dias|semana|semanas|mes|meses|trimestre|trimestres|año|años|ano|anos)$/u)(input);
448
+ const bare = String$1.match(/^([1-9]\d*) (día|días|dias|semana|semanas|mes|meses|trimestre|trimestres|año|años|ano|anos)$/u)(input);
449
+ const future = String$1.match(/^(?:(?:los|las) )?(?:próximos|proximos|próximas|proximas|siguientes) ([1-9]\d*) (día|días|dias|semana|semanas|mes|meses|trimestre|trimestres|año|años|ano|anos)$/u)(input);
450
+ const match = Option.firstSomeOf([
451
+ sinceAgo,
452
+ past,
453
+ bare,
454
+ future
455
+ ]);
456
+ if (Option.isNone(match)) return Option.none();
457
+ const amount = parseTrailingCount(textAt(match.value, 1));
458
+ const unit = countedUnit(textAt(match.value, 2));
459
+ if (Option.isNone(amount) || unit === void 0) return Option.none();
460
+ const entry = units.find((item) => item.unit === unit);
461
+ if (entry === void 0) return Option.none();
462
+ const isFuture = Option.isSome(future);
463
+ const range = isFuture ? futureRange(amount.value, unit) : trailingRange(amount.value, unit);
464
+ const direction = isFuture ? "próximos" : "últimos";
465
+ const noun = amount.value === 1 ? entry.singular : entry.plural;
466
+ return Option.some(candidate(range, `${direction} ${amount.value} ${noun}`));
467
+ };
468
+ const boundaryCandidate = (input) => openBoundaryCandidate(input, [
469
+ ["hasta antes de ", "before"],
470
+ ["hasta el inicio de ", "before"],
471
+ ["hasta e incluyendo ", "through"],
472
+ ["hasta inclusive ", "through"],
473
+ ["a partir de ", "since"],
474
+ ["desde ", "since"],
475
+ ["antes de ", "before"],
476
+ ["hasta ", "through"],
477
+ ["después de ", "after"],
478
+ ["despues de ", "after"]
479
+ ], parsePeriod);
480
+ const parseSpanish = (input) => {
481
+ const remaining = remainingPhrases.find((entry) => entry.phrase === input);
482
+ if (remaining !== void 0) return Option.some(candidate(remainingPeriodRange(remaining.entry.unit), remaining.entry.remaining));
483
+ if (["hasta hoy", "hasta ahora"].includes(input)) return Option.some(candidate(untilNowRange(), "hasta ahora"));
484
+ if ([
485
+ "desde ahora",
486
+ "a partir de ahora",
487
+ "de ahora en adelante"
488
+ ].includes(input)) return Option.some(candidate(fromNowRange(), "desde ahora"));
489
+ const offset = parseCalendarOffset(input);
490
+ if (Option.isSome(offset)) return offset;
491
+ const rolling = parseRollingPeriod(input);
492
+ if (Option.isSome(rolling)) return rolling;
493
+ const toDate = toDatePhrases.find((entry) => entry.phrase === input);
494
+ if (toDate !== void 0) return Option.some(candidate(periodToDateRange(toDate.entry.unit), toDate.entry.toDate));
495
+ const nowBounded = joinedNowCandidate(input, [
496
+ ["desde ", " hasta ahora"],
497
+ ["desde ", " hasta hoy"],
498
+ ["entre ", " y hoy"]
499
+ ], [
500
+ "desde ahora hasta ",
501
+ "desde hoy hasta ",
502
+ "entre hoy y "
503
+ ], parsePeriod, (period) => `desde ${period} hasta ahora`, (period) => `desde ahora hasta ${period}`);
504
+ if (Option.isSome(nowBounded)) return nowBounded;
505
+ const bounded = joinedPeriodCandidate(input, [
506
+ ["desde ", " hasta "],
507
+ ["de ", " a "],
508
+ ["entre ", " y "],
509
+ ["", " - "],
510
+ ["", " – "],
511
+ ["", " — "],
512
+ ["", " hasta "]
513
+ ], parsePeriod, (lower, upper) => `desde ${lower} hasta ${upper}`);
514
+ if (Option.isSome(bounded)) return bounded;
515
+ const boundary = boundaryCandidate(input);
516
+ if (Option.isSome(boundary)) return boundary;
517
+ return Option.map(parsePeriod(input), (period) => candidate(periodRange(period), period.canonical));
518
+ };
519
+ const staticPeriodPhrases = [
520
+ ...periodAliases.map((entry) => entry[0]),
521
+ "este fin de semana",
522
+ "el fin de semana pasado",
523
+ "el próximo fin de semana",
524
+ "el fin de semana anterior al pasado",
525
+ "el fin de semana después del próximo",
526
+ ...periodAliases.flatMap((entry) => [`inicio de ${entry[0]}`, `final de ${entry[0]}`]),
527
+ ...[
528
+ 1,
529
+ 2,
530
+ 3,
531
+ 4
532
+ ].flatMap((quarter) => [
533
+ `t${quarter}`,
534
+ `t${quarter} de este año`,
535
+ `t${quarter} del año pasado`,
536
+ `t${quarter} del próximo año`
537
+ ]),
538
+ ...months.flatMap((month) => [
539
+ month,
540
+ `${month} del año pasado`,
541
+ `${month} del próximo año`
542
+ ])
543
+ ];
544
+ const staticPeriods = periodsFromPhrases(staticPeriodPhrases, parsePeriod);
545
+ const boundaryPrefixes = [
546
+ "desde ",
547
+ "antes de ",
548
+ "hasta ",
549
+ "después de "
550
+ ];
551
+ const countedSuggestions = (input) => {
552
+ const amount = naturalCount(input);
553
+ if (amount === void 0) return [];
554
+ return units.flatMap((entry) => {
555
+ const noun = amount === 1 ? entry.singular : entry.plural;
556
+ return [
557
+ `últimos ${amount} ${noun}`,
558
+ `${amount} ${noun}`,
559
+ `próximos ${amount} ${noun}`,
560
+ `hace ${amount} ${noun}`,
561
+ `dentro de ${amount} ${noun}`
562
+ ];
563
+ });
564
+ };
565
+ const spanishSuggestionPhrases = [
566
+ ...units.map((entry) => entry.toDate),
567
+ ...units.map((entry) => entry.remaining),
568
+ ...staticPeriodPhrases,
569
+ ...prefixNaturalPhrases(staticPeriodPhrases, boundaryPrefixes),
570
+ "hasta ahora",
571
+ "desde ahora"
572
+ ];
573
+ const suggestSpanish = (input, limit) => {
574
+ const fixed = fixedCalendarPeriodPhrases(input, months);
575
+ return completeNaturalPhrases(input, [
576
+ ...spanishSuggestionPhrases,
577
+ ...fixed,
578
+ ...prefixNaturalPhrases(fixed, boundaryPrefixes),
579
+ ...countedSuggestions(input)
580
+ ], limit);
581
+ };
582
+ const renderSpanish = (range) => {
583
+ const offset = calendarPeriodOffset(range);
584
+ if (Option.isSome(offset) && Math.abs(offset.value.amount) > 1) {
585
+ const entry = units.find((unit) => unit.unit === offset.value.unit);
586
+ if (entry !== void 0) {
587
+ const amount = Math.abs(offset.value.amount);
588
+ const noun = amount === 1 ? entry.singular : entry.plural;
589
+ return Option.some(offset.value.amount < 0 ? `hace ${amount} ${noun}` : `dentro de ${amount} ${noun}`);
590
+ }
591
+ }
592
+ const future = futurePeriod(range);
593
+ if (Option.isSome(future)) {
594
+ const entry = units.find((unit) => unit.unit === future.value.unit);
595
+ if (entry !== void 0) {
596
+ const noun = future.value.amount === 1 ? entry.singular : entry.plural;
597
+ return Option.some(`próximos ${future.value.amount} ${noun}`);
598
+ }
599
+ }
600
+ const trailing = trailingPeriod(range);
601
+ if (Option.isSome(trailing)) {
602
+ const entry = units.find((unit) => unit.unit === trailing.value.unit);
603
+ if (entry !== void 0) {
604
+ const noun = trailing.value.amount === 1 ? entry.singular : entry.plural;
605
+ return Option.some(`últimos ${trailing.value.amount} ${noun}`);
606
+ }
607
+ }
608
+ const periods = [...staticPeriods, ...periodsFromPhrases([...datedPeriods(range, months), ...datedQuarterPeriods(range)], parsePeriod)];
609
+ return renderPeriodRange(range, [...units.map((entry) => candidate(periodToDateRange(entry.unit), entry.toDate)), ...units.map((entry) => candidate(remainingPeriodRange(entry.unit), entry.remaining))], periods, (period) => `desde ${period}`, (period) => `antes de ${period}`, (period) => `hasta ${period}`, (period) => `después de ${period}`, (lower, upper) => `desde ${lower} hasta ${upper}`, (period) => `desde ${period} hasta ahora`, (period) => `desde ahora hasta ${period}`, () => "hasta ahora", () => "desde ahora");
610
+ };
611
+ const SpanishContribution = new BaseLanguageContribution({
612
+ locale: "es",
613
+ vocabulary: [
614
+ ...months,
615
+ ...monthAbbreviations.flatMap((aliases) => aliases),
616
+ ...units.flatMap((entry) => [
617
+ entry.singular,
618
+ entry.plural,
619
+ ...entry.current.split(" "),
620
+ ...entry.previous.split(" "),
621
+ ...entry.next.split(" ")
622
+ ]),
623
+ ...toDatePhrases.flatMap((entry) => entry.phrase.split(" ")),
624
+ ...remainingPhrases.flatMap((entry) => entry.phrase.split(" ")),
625
+ "a",
626
+ "ahora",
627
+ "antes",
628
+ "año",
629
+ "comienzo",
630
+ "de",
631
+ "desde",
632
+ "después",
633
+ "durante",
634
+ "entre",
635
+ "fecha",
636
+ "final",
637
+ "fin",
638
+ "hace",
639
+ "hasta",
640
+ "incluyendo",
641
+ "inicio",
642
+ "pasado",
643
+ "principio",
644
+ "próximo",
645
+ "resto",
646
+ "últimos"
647
+ ],
648
+ normalize: normalizeNaturalText,
649
+ correct: correctWhitespaceSeparatedText,
650
+ parseExact: parseSpanish,
651
+ suggest: suggestSpanish,
652
+ render: renderSpanish
653
+ });
654
+ const SpanishLanguage = defineLanguagePlugin({
655
+ id: "chronolizer/language-es",
656
+ effect: (context) => Effect.asVoid(context.register("chronolizer/language-es", SpanishContribution))
657
+ });
658
+ const SpanishLanguageLayer = languagePluginsLayer([SpanishLanguage]);
659
+ //#endregion
660
+ export { SpanishContribution, SpanishLanguage, SpanishLanguageLayer };
@@ -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/fr.d.ts
6
+ declare const FrenchContribution: BaseLanguageContribution;
7
+ declare const FrenchLanguage: LanguagePlugin;
8
+ declare const FrenchLanguageLayer: import("effect/Layer").Layer<LanguageRegistry, LanguageConflictError | LanguageRegistrationError, never>;
9
+ //#endregion
10
+ export { FrenchContribution, FrenchLanguage, FrenchLanguageLayer };