chronolizer 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +184 -119
  2. package/dist/ast/constructors.d.mts +135 -0
  3. package/dist/ast/constructors.mjs +25 -0
  4. package/dist/ast/fold.d.mts +13 -0
  5. package/dist/ast/fold.mjs +58 -0
  6. package/dist/ast/normalize.d.mts +42 -0
  7. package/dist/ast/normalize.mjs +34 -0
  8. package/dist/ast/schemas.d.mts +80 -0
  9. package/dist/ast/schemas.mjs +65 -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 +76 -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 +50 -0
  26. package/dist/language/registry.d.mts +17 -0
  27. package/dist/language/registry.mjs +116 -0
  28. package/dist/locales/cs.d.mts +10 -0
  29. package/dist/locales/cs.mjs +746 -0
  30. package/dist/locales/de.d.mts +10 -0
  31. package/dist/locales/de.mjs +1039 -0
  32. package/dist/locales/en.d.mts +10 -0
  33. package/dist/locales/en.mjs +598 -0
  34. package/dist/locales/es.d.mts +10 -0
  35. package/dist/locales/es.mjs +908 -0
  36. package/dist/locales/fr.d.mts +10 -0
  37. package/dist/locales/fr.mjs +901 -0
  38. package/dist/locales/nl.d.mts +10 -0
  39. package/dist/locales/nl.mjs +791 -0
  40. package/dist/locales/pl.d.mts +10 -0
  41. package/dist/locales/pl.mjs +885 -0
  42. package/dist/locales/shared.mjs +395 -0
  43. package/dist/locales/tr.d.mts +10 -0
  44. package/dist/locales/tr.mjs +725 -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 +58 -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,725 @@
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 { absoluteDatePeriod, calendarPeriodOffset, candidate, currentYearDatePeriods, datedPeriods, datedQuarterPeriods, fixedDatePeriod, fixedMonthPeriod, fixedQuarterPeriod, fixedYearPeriod, fromNowRange, futurePeriod, futureRange, isoDate, joinedNowCandidate, joinedPeriodCandidate, monthOfRelativeYear, namedCurrentYearDatePeriod, namedDatePeriod, openBoundaryCandidate, parseTrailingCount, periodBoundaryCandidate, 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/tr.ts
10
+ const months = [
11
+ "ocak",
12
+ "şubat",
13
+ "mart",
14
+ "nisan",
15
+ "mayıs",
16
+ "haziran",
17
+ "temmuz",
18
+ "ağustos",
19
+ "eylül",
20
+ "ekim",
21
+ "kasım",
22
+ "aralık"
23
+ ];
24
+ const monthAbbreviations = [
25
+ ["oca"],
26
+ ["şub", "sub"],
27
+ ["mar"],
28
+ ["nis"],
29
+ ["may"],
30
+ ["haz"],
31
+ ["tem"],
32
+ ["ağu", "agu"],
33
+ ["eyl"],
34
+ ["eki"],
35
+ ["kas"],
36
+ ["ara"]
37
+ ];
38
+ const units = [
39
+ {
40
+ unit: "day",
41
+ singular: "gün",
42
+ plural: "gün",
43
+ current: "bugün",
44
+ previous: "dün",
45
+ next: "yarın",
46
+ toDate: "bugün şimdiye kadar",
47
+ remaining: "günün geri kalanı"
48
+ },
49
+ {
50
+ unit: "week",
51
+ singular: "hafta",
52
+ plural: "hafta",
53
+ current: "bu hafta",
54
+ previous: "geçen hafta",
55
+ next: "gelecek hafta",
56
+ toDate: "hafta başından bugüne",
57
+ remaining: "haftanın geri kalanı"
58
+ },
59
+ {
60
+ unit: "month",
61
+ singular: "ay",
62
+ plural: "ay",
63
+ current: "bu ay",
64
+ previous: "geçen ay",
65
+ next: "gelecek ay",
66
+ toDate: "ay başından bugüne",
67
+ remaining: "ayın geri kalanı"
68
+ },
69
+ {
70
+ unit: "quarter",
71
+ singular: "çeyrek",
72
+ plural: "çeyrek",
73
+ current: "bu çeyrek",
74
+ previous: "geçen çeyrek",
75
+ next: "gelecek çeyrek",
76
+ toDate: "çeyrek başından bugüne",
77
+ remaining: "çeyreğin geri kalanı"
78
+ },
79
+ {
80
+ unit: "year",
81
+ singular: "yıl",
82
+ plural: "yıl",
83
+ current: "bu yıl",
84
+ previous: "geçen yıl",
85
+ next: "gelecek yıl",
86
+ toDate: "yılbaşından bugüne",
87
+ remaining: "yılın geri kalanı"
88
+ }
89
+ ];
90
+ const title = (value) => `${value.slice(0, 1).toLocaleUpperCase("tr")}${value.slice(1)}`;
91
+ const unitAliases = [
92
+ ["gün", "day"],
93
+ ["gun", "day"],
94
+ ["hafta", "week"],
95
+ ["ay", "month"],
96
+ ["çeyrek", "quarter"],
97
+ ["ceyrek", "quarter"],
98
+ ["yıl", "year"],
99
+ ["yil", "year"],
100
+ ["sene", "year"]
101
+ ];
102
+ const periodAliases = [
103
+ ...units.flatMap((entry) => [
104
+ [
105
+ entry.current,
106
+ entry.unit,
107
+ 0,
108
+ entry.current
109
+ ],
110
+ [
111
+ entry.previous,
112
+ entry.unit,
113
+ -1,
114
+ entry.previous
115
+ ],
116
+ [
117
+ entry.next,
118
+ entry.unit,
119
+ 1,
120
+ entry.next
121
+ ]
122
+ ]),
123
+ [
124
+ "önceki hafta",
125
+ "week",
126
+ -1,
127
+ "geçen hafta"
128
+ ],
129
+ [
130
+ "geçtiğimiz hafta",
131
+ "week",
132
+ -1,
133
+ "geçen hafta"
134
+ ],
135
+ [
136
+ "önümüzdeki hafta",
137
+ "week",
138
+ 1,
139
+ "gelecek hafta"
140
+ ],
141
+ [
142
+ "önceki ay",
143
+ "month",
144
+ -1,
145
+ "geçen ay"
146
+ ],
147
+ [
148
+ "bir önceki ay",
149
+ "month",
150
+ -1,
151
+ "geçen ay"
152
+ ],
153
+ [
154
+ "geçtiğimiz ay",
155
+ "month",
156
+ -1,
157
+ "geçen ay"
158
+ ],
159
+ [
160
+ "önümüzdeki ay",
161
+ "month",
162
+ 1,
163
+ "gelecek ay"
164
+ ],
165
+ [
166
+ "sonraki ay",
167
+ "month",
168
+ 1,
169
+ "gelecek ay"
170
+ ],
171
+ [
172
+ "bir sonraki ay",
173
+ "month",
174
+ 1,
175
+ "gelecek ay"
176
+ ],
177
+ [
178
+ "önceki yıl",
179
+ "year",
180
+ -1,
181
+ "geçen yıl"
182
+ ],
183
+ [
184
+ "geçtiğimiz yıl",
185
+ "year",
186
+ -1,
187
+ "geçen yıl"
188
+ ],
189
+ [
190
+ "önümüzdeki yıl",
191
+ "year",
192
+ 1,
193
+ "gelecek yıl"
194
+ ],
195
+ [
196
+ "evvelsi gün",
197
+ "day",
198
+ -2,
199
+ "evvelsi gün"
200
+ ],
201
+ [
202
+ "öbür gün",
203
+ "day",
204
+ 2,
205
+ "öbür gün"
206
+ ],
207
+ [
208
+ "geçen haftadan önceki hafta",
209
+ "week",
210
+ -2,
211
+ "geçen haftadan önceki hafta"
212
+ ],
213
+ [
214
+ "gelecek haftadan sonraki hafta",
215
+ "week",
216
+ 2,
217
+ "gelecek haftadan sonraki hafta"
218
+ ],
219
+ [
220
+ "geçen aydan önceki ay",
221
+ "month",
222
+ -2,
223
+ "geçen aydan önceki ay"
224
+ ],
225
+ [
226
+ "gelecek aydan sonraki ay",
227
+ "month",
228
+ 2,
229
+ "gelecek aydan sonraki ay"
230
+ ],
231
+ [
232
+ "geçen çeyrekten önceki çeyrek",
233
+ "quarter",
234
+ -2,
235
+ "geçen çeyrekten önceki çeyrek"
236
+ ],
237
+ [
238
+ "gelecek çeyrekten sonraki çeyrek",
239
+ "quarter",
240
+ 2,
241
+ "gelecek çeyrekten sonraki çeyrek"
242
+ ],
243
+ [
244
+ "geçen yıldan önceki yıl",
245
+ "year",
246
+ -2,
247
+ "geçen yıldan önceki yıl"
248
+ ],
249
+ [
250
+ "gelecek yıldan sonraki yıl",
251
+ "year",
252
+ 2,
253
+ "gelecek yıldan sonraki yıl"
254
+ ]
255
+ ];
256
+ const periodGenitive = {
257
+ day: "günün",
258
+ week: "haftanın",
259
+ month: "ayın",
260
+ quarter: "çeyreğin",
261
+ year: "yılın"
262
+ };
263
+ const toDatePhrases = units.flatMap((entry) => {
264
+ const yearAliases = entry.unit === "year" ? [
265
+ "yılbaşından bu yana",
266
+ "yılın başından bugüne",
267
+ "bu yıl şimdiye kadar"
268
+ ] : [];
269
+ return [
270
+ entry.toDate,
271
+ `${periodGenitive[entry.unit]} başından bugüne`,
272
+ `${entry.current} şimdiye kadar`,
273
+ ...yearAliases
274
+ ].map((phrase) => ({
275
+ entry,
276
+ phrase
277
+ }));
278
+ });
279
+ const remainingPhrases = units.map((entry) => ({
280
+ entry,
281
+ phrase: entry.remaining
282
+ }));
283
+ const relativeYearDirection = (value) => {
284
+ if (value.includes("geçen") || value.includes("önceki")) return -1;
285
+ if (value.includes("gelecek") || value.includes("önümüzdeki")) return 1;
286
+ return 0;
287
+ };
288
+ const relativeYearName = (direction) => {
289
+ if (direction < 0) return "geçen yıl";
290
+ if (direction > 0) return "gelecek yıl";
291
+ return "bu yıl";
292
+ };
293
+ const monthNumber = (value) => {
294
+ const normalized = value.endsWith(".") ? value.slice(0, -1) : value;
295
+ const full = months.findIndex((month) => month === normalized);
296
+ if (full !== -1) return full + 1;
297
+ const short = monthAbbreviations.findIndex((aliases) => aliases.some((alias) => alias === normalized));
298
+ return short === -1 ? void 0 : short + 1;
299
+ };
300
+ const dateLabel = (day, month, year) => `${day} ${textAt(months, month - 1)} ${year}`;
301
+ const currentDateLabel = (day, month) => `${day} ${textAt(months, month - 1)}`;
302
+ const parseNamedDate = (input) => {
303
+ const named = String$1.match(/^([0-3]?\d)(?:\.)? ([a-zçğıöşü]+\.?)(?:,)? (\d{4})$/u)(input);
304
+ if (Option.isSome(named)) return namedDatePeriod(textAt(named.value, 3), textAt(named.value, 2), textAt(named.value, 1), monthNumber, dateLabel);
305
+ const numeric = String$1.match(/^([0-3]?\d)[./-]([01]?\d)[./-](\d{4})$/u)(input);
306
+ if (Option.isSome(numeric)) {
307
+ const year = validYear(textAt(numeric.value, 3));
308
+ const month = Number(textAt(numeric.value, 2));
309
+ const day = Number(textAt(numeric.value, 1));
310
+ if (year !== void 0 && month >= 1 && month <= 12) {
311
+ const value = isoDate(year, month, day);
312
+ if (isIsoDate(value) && value !== "9999-12-31") return Option.some(fixedDatePeriod(value, dateLabel(day, month, year)));
313
+ }
314
+ }
315
+ const current = String$1.match(/^([0-3]?\d)(?:\.)? ([a-zçğıöşü]+\.?)$/u)(input);
316
+ return Option.isSome(current) ? namedCurrentYearDatePeriod(textAt(current.value, 2), textAt(current.value, 1), monthNumber, currentDateLabel) : Option.none();
317
+ };
318
+ const quarterNumber = (value) => {
319
+ if ((value.startsWith("q") || value.startsWith("ç")) && value.length === 2) return Number(value.slice(1));
320
+ if (value === "1." || value === "birinci" || value === "ilk") return 1;
321
+ if (value === "2." || value === "ikinci") return 2;
322
+ if (value === "3." || value === "üçüncü" || value === "ucuncu") return 3;
323
+ return value === "4." || value === "dördüncü" || value === "dorduncu" ? 4 : void 0;
324
+ };
325
+ const parseQuarter = (input) => {
326
+ const fixed = String$1.match(/^(ç[1-4]|q[1-4]|1\.|birinci|ilk|2\.|ikinci|3\.|üçüncü|ucuncu|4\.|dördüncü|dorduncu)(?: çeyrek)?(?:,)? (\d{4})$/u)(input);
327
+ if (Option.isSome(fixed)) {
328
+ const quarter = quarterNumber(textAt(fixed.value, 1));
329
+ const year = validYear(textAt(fixed.value, 2));
330
+ if (quarter !== void 0 && year !== void 0) return Option.some(fixedQuarterPeriod(year, quarter, `Ç${quarter} ${year}`));
331
+ }
332
+ const relative = String$1.match(/^(?:bu yıl )?(ç[1-4]|q[1-4])(?: (geçen yıl|gelecek yıl|bu yıl))?$/u)(input);
333
+ if (Option.isSome(relative)) {
334
+ const quarter = quarterNumber(textAt(relative.value, 1));
335
+ const direction = relativeYearDirection(textAt(relative.value, 2));
336
+ if (quarter !== void 0) return Option.some(quarterOfRelativeYear(quarter, direction, `Ç${quarter} ${relativeYearName(direction)}`));
337
+ }
338
+ const standalone = String$1.match(/^(ç[1-4]|q[1-4])$/u)(input);
339
+ if (Option.isNone(standalone)) return Option.none();
340
+ const quarter = quarterNumber(textAt(standalone.value, 1));
341
+ return quarter === void 0 ? Option.none() : Option.some(quarterOfRelativeYear(quarter, 0, `Ç${quarter}`));
342
+ };
343
+ const parseBasePeriod = (input) => {
344
+ const absoluteDate = absoluteDatePeriod(input, "tr");
345
+ if (Option.isSome(absoluteDate)) return absoluteDate;
346
+ const namedDate = parseNamedDate(input);
347
+ if (Option.isSome(namedDate)) return namedDate;
348
+ const quarter = parseQuarter(input);
349
+ if (Option.isSome(quarter)) return quarter;
350
+ const yearMatch = String$1.match(/^(?:yıl |yılı )?(\d{4})$/u)(input);
351
+ if (Option.isSome(yearMatch)) {
352
+ const year = validYear(textAt(yearMatch.value, 1));
353
+ if (year !== void 0) return Option.some(fixedYearPeriod(year, String(year)));
354
+ }
355
+ const monthYear = String$1.match(/^([a-zçğıöşü]+\.?)(?:,)? (\d{4})$/u)(input);
356
+ if (Option.isSome(monthYear)) {
357
+ const month = monthNumber(textAt(monthYear.value, 1));
358
+ const year = validYear(textAt(monthYear.value, 2));
359
+ if (month !== void 0 && year !== void 0) return Option.some(fixedMonthPeriod(year, month, `${title(textAt(months, month - 1))} ${year}`));
360
+ }
361
+ const relativeMonth = String$1.match(/^([a-zçğıöşü]+\.?) (geçen yıl|gelecek yıl|bu yıl)$/u)(input);
362
+ const relativeMonthYearFirst = String$1.match(/^(geçen yıl|gelecek yıl|bu yıl) ([a-zçğıöşü]+\.?)$/u)(input);
363
+ const relativeMatch = Option.firstSomeOf([relativeMonth, relativeMonthYearFirst]);
364
+ if (Option.isSome(relativeMatch)) {
365
+ const yearFirst = Option.isSome(relativeMonthYearFirst);
366
+ const monthText = textAt(relativeMatch.value, yearFirst ? 2 : 1);
367
+ const yearText = textAt(relativeMatch.value, yearFirst ? 1 : 2);
368
+ const month = monthNumber(monthText);
369
+ const direction = relativeYearDirection(yearText);
370
+ if (month !== void 0) return Option.some(monthOfRelativeYear(month, direction, `${title(textAt(months, month - 1))} ${relativeYearName(direction)}`));
371
+ }
372
+ const standaloneMonth = monthNumber(input);
373
+ if (standaloneMonth !== void 0) return Option.some(monthOfRelativeYear(standaloneMonth, 0, title(textAt(months, standaloneMonth - 1))));
374
+ const alias = periodAliases.find((entry) => entry[0] === input);
375
+ if (alias !== void 0) return Option.some(relativePeriod(alias[1], alias[2], alias[3]));
376
+ if (["hafta sonu", "bu hafta sonu"].includes(input)) return Option.some(relativeWeekend(0, "bu hafta sonu"));
377
+ if (["geçen hafta sonu", "önceki hafta sonu"].includes(input)) return Option.some(relativeWeekend(-1, "geçen hafta sonu"));
378
+ if (["gelecek hafta sonu", "önümüzdeki hafta sonu"].includes(input)) return Option.some(relativeWeekend(1, "gelecek hafta sonu"));
379
+ if (input === "geçen hafta sonundan önceki hafta sonu") return Option.some(relativeWeekend(-2, input));
380
+ if (input === "gelecek hafta sonundan sonraki hafta sonu") return Option.some(relativeWeekend(2, input));
381
+ return Option.none();
382
+ };
383
+ const parsePeriod = (input) => {
384
+ const edge = String$1.match(/^(.+?)(?:ın|in|un|ün) (başı|başlangıcı|sonu)$/u)(input);
385
+ if (Option.isSome(edge)) {
386
+ const period = parseBasePeriod(textAt(edge.value, 1));
387
+ if (Option.isSome(period)) {
388
+ const isEnd = textAt(edge.value, 2) === "sonu";
389
+ const canonical = `${period.value.canonical} ${isEnd ? "sonu" : "başı"}`;
390
+ return Option.some(isEnd ? periodEndDay(period.value, canonical) : periodStartDay(period.value, canonical));
391
+ }
392
+ }
393
+ const wrapper = [
394
+ "boyunca ",
395
+ "içinde ",
396
+ "tüm "
397
+ ].find((prefix) => input.startsWith(prefix));
398
+ return parseBasePeriod(wrapper === void 0 ? input : input.slice(wrapper.length));
399
+ };
400
+ const countedUnit = (value) => unitAliases.find((entry) => entry[0] === value)?.[1];
401
+ const countedUnitPattern = "gün|gun|hafta|ay|çeyrek|ceyrek|yıl|yil|sene";
402
+ const countedPattern = (source) => new RegExp(source.replace("UNIT", countedUnitPattern), "u");
403
+ const calendarPastPattern = countedPattern("^([1-9]\\d*) (UNIT) önce$");
404
+ const calendarFuturePatterns = [countedPattern("^([1-9]\\d*) (UNIT) sonra$"), countedPattern("^([1-9]\\d*) (UNIT) içinde$")];
405
+ const rollingPastPatterns = [countedPattern("^(?:son|geçen|önceki|geçtiğimiz) ([1-9]\\d*) (UNIT)$"), countedPattern("^([1-9]\\d*) (?:son|geçen|önceki) (UNIT)$")];
406
+ const rollingFuturePatterns = [countedPattern("^(?:gelecek|önümüzdeki|sonraki) ([1-9]\\d*) (UNIT)$"), countedPattern("^([1-9]\\d*) (?:gelecek|sonraki) (UNIT)$")];
407
+ const rollingSincePattern = countedPattern("^([1-9]\\d*) (UNIT) boyunca$");
408
+ const rollingBarePattern = countedPattern("^([1-9]\\d*) (UNIT)$");
409
+ const firstPatternMatch = (input, patterns) => Option.firstSomeOf(patterns.map((pattern) => String$1.match(pattern)(input)));
410
+ const singularRollingCanonical = (entry, future) => future ? `önümüzdeki bir ${entry.singular}` : `son bir ${entry.singular}`;
411
+ const singularRollingPhrases = units.flatMap((entry) => [
412
+ {
413
+ phrase: `son bir ${entry.singular}`,
414
+ entry,
415
+ future: false
416
+ },
417
+ {
418
+ phrase: `bir ${entry.singular} boyunca`,
419
+ entry,
420
+ future: false
421
+ },
422
+ {
423
+ phrase: `geçtiğimiz bir ${entry.singular}`,
424
+ entry,
425
+ future: false
426
+ },
427
+ {
428
+ phrase: `önümüzdeki bir ${entry.singular}`,
429
+ entry,
430
+ future: true
431
+ },
432
+ {
433
+ phrase: `bugünden itibaren bir ${entry.singular} boyunca`,
434
+ entry,
435
+ future: true
436
+ }
437
+ ]);
438
+ const singularCalendarOffsets = units.flatMap((entry) => [{
439
+ phrase: `bir ${entry.singular} önce`,
440
+ entry,
441
+ direction: -1
442
+ }, {
443
+ phrase: `bir ${entry.singular} sonra`,
444
+ entry,
445
+ direction: 1
446
+ }]);
447
+ const parseCalendarOffset = (input) => {
448
+ const singular = singularCalendarOffsets.find((entry) => entry.phrase === input);
449
+ if (singular !== void 0) return Option.some(candidate(periodRange(relativePeriod(singular.entry.unit, singular.direction, singular.phrase)), singular.phrase));
450
+ const past = String$1.match(calendarPastPattern)(input);
451
+ const future = firstPatternMatch(input, calendarFuturePatterns);
452
+ const match = Option.firstSomeOf([past, future]);
453
+ if (Option.isNone(match)) return Option.none();
454
+ const amount = parseTrailingCount(textAt(match.value, 1));
455
+ const unit = countedUnit(textAt(match.value, 2));
456
+ if (Option.isNone(amount) || unit === void 0) return Option.none();
457
+ const entry = units.find((item) => item.unit === unit);
458
+ if (entry === void 0) return Option.none();
459
+ const direction = Option.isSome(past) ? -amount.value : amount.value;
460
+ const noun = amount.value === 1 ? entry.singular : entry.plural;
461
+ const canonical = direction < 0 ? `${amount.value} ${noun} önce` : `${amount.value} ${noun} sonra`;
462
+ return Option.some(candidate(periodRange(relativePeriod(unit, direction, canonical)), canonical));
463
+ };
464
+ const parseRollingPeriod = (input) => {
465
+ const singular = singularRollingPhrases.find((entry) => entry.phrase === input);
466
+ if (singular !== void 0) {
467
+ const range = singular.future ? futureRange(1, singular.entry.unit) : trailingRange(1, singular.entry.unit);
468
+ return Option.some(candidate(range, singularRollingCanonical(singular.entry, singular.future)));
469
+ }
470
+ const since = String$1.match(rollingSincePattern)(input);
471
+ const past = firstPatternMatch(input, rollingPastPatterns);
472
+ const bare = String$1.match(rollingBarePattern)(input);
473
+ const future = firstPatternMatch(input, rollingFuturePatterns);
474
+ const match = Option.firstSomeOf([
475
+ since,
476
+ past,
477
+ bare,
478
+ future
479
+ ]);
480
+ if (Option.isNone(match)) return Option.none();
481
+ const amount = parseTrailingCount(textAt(match.value, 1));
482
+ const unit = countedUnit(textAt(match.value, 2));
483
+ if (Option.isNone(amount) || unit === void 0) return Option.none();
484
+ const entry = units.find((item) => item.unit === unit);
485
+ if (entry === void 0) return Option.none();
486
+ const isFuture = Option.isSome(future);
487
+ const range = isFuture ? futureRange(amount.value, unit) : trailingRange(amount.value, unit);
488
+ if (amount.value === 1) return Option.some(candidate(range, singularRollingCanonical(entry, isFuture)));
489
+ const modifier = isFuture ? "gelecek" : "son";
490
+ return Option.some(candidate(range, `${modifier} ${amount.value} ${entry.plural}`));
491
+ };
492
+ const parseElidedDateRange = (input) => {
493
+ const full = String$1.match(/^([0-3]?\d)(?:\.)? ([a-zçğıöşü]+\.?)'?(?:den|dan|ten|tan) ([0-3]?\d)(?:\.)? ([a-zçğıöşü]+\.?)'?(?:e|a|ye|ya) kadar$/u)(input);
494
+ if (Option.isSome(full)) return joinedPeriodCandidate(`${textAt(full.value, 1)} ${textAt(full.value, 2)} ile ${textAt(full.value, 3)} ${textAt(full.value, 4)}`, [["", " ile "]], parsePeriod, (lower, upper) => `${lower} ile ${upper} arası`);
495
+ const elided = String$1.match(/^([0-3]?\d)(?:\.)? ([a-zçğıöşü]+\.?)'?(?:den|dan|ten|tan) ([0-3]?\d)'?(?:sine|sına|üne|una|ine|ına) kadar$/u)(input);
496
+ const dashed = String$1.match(/^([0-3]?\d)[–—-]([0-3]?\d) ([a-zçğıöşü]+\.?)$/u)(input);
497
+ const match = Option.firstSomeOf([elided, dashed]);
498
+ if (Option.isNone(match)) return Option.none();
499
+ const isElided = Option.isSome(elided);
500
+ const lowerDay = textAt(match.value, 1);
501
+ const upperDay = textAt(match.value, isElided ? 3 : 2);
502
+ const month = textAt(match.value, isElided ? 2 : 3);
503
+ return joinedPeriodCandidate(`${lowerDay} ${month} ile ${upperDay} ${month}`, [["", " ile "]], parsePeriod, (lower, upper) => `${lower} ile ${upper} arası`);
504
+ };
505
+ const suffixBoundary = (input) => {
506
+ const boundaries = [
507
+ [String$1.match(/^(.+?)'?(?:den|dan|ten|tan) itibaren$/u)(input), "since"],
508
+ [String$1.match(/^(.+) itibarıyla$/u)(input), "since"],
509
+ [String$1.match(/^(.+?)'?(?:den|dan|ten|tan) önce$/u)(input), "before"],
510
+ [String$1.match(/^(.+) öncesi$/u)(input), "before"],
511
+ [String$1.match(/^(.+?)'?(?:e|a|ye|ya) kadar$/u)(input), "through"],
512
+ [String$1.match(/^(.+) sonuna kadar$/u)(input), "through"],
513
+ [String$1.match(/^(.+?)'?(?:den|dan|ten|tan) sonra$/u)(input), "after"],
514
+ [String$1.match(/^(.+) sonrası$/u)(input), "after"]
515
+ ];
516
+ for (const [match, boundary] of boundaries) {
517
+ if (Option.isNone(match)) continue;
518
+ const period = parsePeriod(textAt(match.value, 1));
519
+ if (Option.isSome(period)) return Option.some(periodBoundaryCandidate(period.value, boundary, input));
520
+ }
521
+ return Option.none();
522
+ };
523
+ const boundaryCandidate = (input) => {
524
+ const suffix = suffixBoundary(input);
525
+ if (Option.isSome(suffix)) return suffix;
526
+ return openBoundaryCandidate(input, [
527
+ ["itibaren ", "since"],
528
+ ["önce ", "before"],
529
+ ["kadar ", "through"],
530
+ ["sonra ", "after"]
531
+ ], parsePeriod);
532
+ };
533
+ const parseTurkish = (input) => {
534
+ const remaining = remainingPhrases.find((entry) => entry.phrase === input);
535
+ if (remaining !== void 0) return Option.some(candidate(remainingPeriodRange(remaining.entry.unit), remaining.entry.remaining));
536
+ if ([
537
+ "bugüne kadar",
538
+ "şimdiye kadar",
539
+ "şu ana kadar"
540
+ ].includes(input)) return Option.some(candidate(untilNowRange(), "şimdiye kadar"));
541
+ if ([
542
+ "bugünden itibaren",
543
+ "şu andan itibaren",
544
+ "bundan sonra"
545
+ ].includes(input)) return Option.some(candidate(fromNowRange(), "bugünden itibaren"));
546
+ const offset = parseCalendarOffset(input);
547
+ if (Option.isSome(offset)) return offset;
548
+ const rolling = parseRollingPeriod(input);
549
+ if (Option.isSome(rolling)) return rolling;
550
+ const toDate = toDatePhrases.find((entry) => entry.phrase === input);
551
+ if (toDate !== void 0) return Option.some(candidate(periodToDateRange(toDate.entry.unit), toDate.entry.toDate));
552
+ const elided = parseElidedDateRange(input);
553
+ if (Option.isSome(elided)) return elided;
554
+ const joinedInput = input.endsWith(" arası") ? input.slice(0, -6) : input;
555
+ const nowBounded = joinedNowCandidate(joinedInput, [["", " ile bugün"], ["", " ve bugün"]], ["bugün ile ", "bugün ve "], parsePeriod, (period) => `${period} ile bugün arası`, (period) => `bugün ile ${period} arası`);
556
+ if (Option.isSome(nowBounded)) return nowBounded;
557
+ const bounded = joinedPeriodCandidate(joinedInput, [
558
+ ["", " ile "],
559
+ ["", " ve "],
560
+ ["", " - "],
561
+ ["", " – "],
562
+ ["", " — "]
563
+ ], parsePeriod, (lower, upper) => `${lower} ile ${upper} arası`);
564
+ if (Option.isSome(bounded)) return bounded;
565
+ const boundary = boundaryCandidate(input);
566
+ if (Option.isSome(boundary)) return boundary;
567
+ return parsePeriod(input).pipe(Option.map((period) => candidate(periodRange(period), period.canonical)));
568
+ };
569
+ const staticPeriodPhrases = [
570
+ ...periodAliases.map((entry) => entry[0]),
571
+ "bu hafta sonu",
572
+ "geçen hafta sonu",
573
+ "gelecek hafta sonu",
574
+ "geçen hafta sonundan önceki hafta sonu",
575
+ "gelecek hafta sonundan sonraki hafta sonu",
576
+ "bu haftanın başı",
577
+ "bu haftanın sonu",
578
+ "geçen haftanın başı",
579
+ "geçen haftanın sonu",
580
+ "gelecek haftanın başı",
581
+ "gelecek haftanın sonu",
582
+ "bu ayın başı",
583
+ "bu ayın sonu",
584
+ "geçen ayın başı",
585
+ "geçen ayın sonu",
586
+ "gelecek ayın başı",
587
+ "gelecek ayın sonu",
588
+ "bu yılın başı",
589
+ "bu yılın sonu",
590
+ "geçen yılın başı",
591
+ "geçen yılın sonu",
592
+ "gelecek yılın başı",
593
+ "gelecek yılın sonu",
594
+ ...[
595
+ 1,
596
+ 2,
597
+ 3,
598
+ 4
599
+ ].flatMap((quarter) => [
600
+ `ç${quarter}`,
601
+ `ç${quarter} bu yıl`,
602
+ `ç${quarter} geçen yıl`,
603
+ `ç${quarter} gelecek yıl`
604
+ ]),
605
+ ...months.flatMap((month) => [
606
+ month,
607
+ `${month} geçen yıl`,
608
+ `${month} gelecek yıl`
609
+ ])
610
+ ];
611
+ const staticPeriods = periodsFromPhrases(staticPeriodPhrases, parsePeriod);
612
+ const boundaryPrefixes = [
613
+ "itibaren ",
614
+ "önce ",
615
+ "kadar ",
616
+ "sonra "
617
+ ];
618
+ const countedSuggestions = (input) => {
619
+ const amount = naturalCount(input);
620
+ if (amount === void 0) return [];
621
+ return units.flatMap((entry) => {
622
+ const noun = amount === 1 ? entry.singular : entry.plural;
623
+ return [
624
+ `son ${amount} ${noun}`,
625
+ `geçen ${amount} ${noun}`,
626
+ `${amount} ${noun}`,
627
+ `gelecek ${amount} ${noun}`,
628
+ `önümüzdeki ${amount} ${noun}`,
629
+ `${amount} ${noun} önce`,
630
+ `${amount} ${noun} sonra`
631
+ ];
632
+ });
633
+ };
634
+ const turkishSuggestionPhrases = [
635
+ ...units.map((entry) => entry.toDate),
636
+ ...units.map((entry) => entry.remaining),
637
+ ...singularRollingPhrases.map((entry) => entry.phrase),
638
+ ...singularCalendarOffsets.map((entry) => entry.phrase),
639
+ ...staticPeriodPhrases,
640
+ ...prefixNaturalPhrases(staticPeriodPhrases, boundaryPrefixes),
641
+ "şimdiye kadar",
642
+ "bugünden itibaren"
643
+ ];
644
+ const suggestTurkish = (input, limit) => {
645
+ const fixed = fixedCalendarPeriodPhrases(input, months).map((phrase) => phrase.startsWith("q") ? `ç${phrase.slice(1)}` : phrase);
646
+ return completeNaturalPhrases(input, [
647
+ ...turkishSuggestionPhrases,
648
+ ...fixed,
649
+ ...prefixNaturalPhrases(fixed, boundaryPrefixes),
650
+ ...countedSuggestions(input)
651
+ ], limit);
652
+ };
653
+ const renderTurkish = (range) => {
654
+ const offset = calendarPeriodOffset(range);
655
+ if (Option.isSome(offset) && Math.abs(offset.value.amount) > 1) {
656
+ const entry = units.find((unit) => unit.unit === offset.value.unit);
657
+ if (entry !== void 0) {
658
+ const amount = Math.abs(offset.value.amount);
659
+ const noun = amount === 1 ? entry.singular : entry.plural;
660
+ return Option.some(offset.value.amount < 0 ? `${amount} ${noun} önce` : `${amount} ${noun} sonra`);
661
+ }
662
+ }
663
+ const future = futurePeriod(range);
664
+ if (Option.isSome(future)) {
665
+ const entry = units.find((unit) => unit.unit === future.value.unit);
666
+ if (entry !== void 0) return Option.some(future.value.amount === 1 ? singularRollingCanonical(entry, true) : `gelecek ${future.value.amount} ${entry.plural}`);
667
+ }
668
+ const trailing = trailingPeriod(range);
669
+ if (Option.isSome(trailing)) {
670
+ const entry = units.find((unit) => unit.unit === trailing.value.unit);
671
+ if (entry !== void 0) return Option.some(trailing.value.amount === 1 ? singularRollingCanonical(entry, false) : `son ${trailing.value.amount} ${entry.plural}`);
672
+ }
673
+ const periods = [
674
+ ...staticPeriods,
675
+ ...currentYearDatePeriods(range, currentDateLabel),
676
+ ...periodsFromPhrases([...datedPeriods(range, months), ...datedQuarterPeriods(range).map((phrase) => phrase.startsWith("Q") ? `Ç${phrase.slice(1)}` : phrase)], parsePeriod)
677
+ ];
678
+ return renderPeriodRange(range, [...units.map((entry) => candidate(periodToDateRange(entry.unit), entry.toDate)), ...units.map((entry) => candidate(remainingPeriodRange(entry.unit), entry.remaining))], periods, (period) => `${period} itibarıyla`, (period) => `${period} öncesi`, (period) => `${period} sonuna kadar`, (period) => `${period} sonrası`, (lower, upper) => `${lower} ile ${upper} arası`, (period) => `${period} ile bugün arası`, (period) => `bugün ile ${period} arası`, () => "şimdiye kadar", () => "bugünden itibaren");
679
+ };
680
+ const normalizeTurkish = (input, locale) => normalizeNaturalText(input, locale).replaceAll("’", "'");
681
+ const TurkishContribution = new BaseLanguageContribution({
682
+ locale: "tr",
683
+ vocabulary: [
684
+ ...months,
685
+ ...monthAbbreviations.flatMap((aliases) => aliases),
686
+ ...units.flatMap((entry) => [
687
+ entry.singular,
688
+ entry.plural,
689
+ ...entry.current.split(" "),
690
+ ...entry.previous.split(" "),
691
+ ...entry.next.split(" ")
692
+ ]),
693
+ ...toDatePhrases.flatMap((entry) => entry.phrase.split(" ")),
694
+ ...remainingPhrases.flatMap((entry) => entry.phrase.split(" ")),
695
+ "arası",
696
+ "başı",
697
+ "bugün",
698
+ "gelecek",
699
+ "geçen",
700
+ "geri",
701
+ "itibaren",
702
+ "itibarıyla",
703
+ "kadar",
704
+ "kalanı",
705
+ "önce",
706
+ "önceki",
707
+ "önümüzdeki",
708
+ "son",
709
+ "sonra",
710
+ "sonrası",
711
+ "şimdiye"
712
+ ],
713
+ normalize: normalizeTurkish,
714
+ correct: correctWhitespaceSeparatedText,
715
+ parseExact: parseTurkish,
716
+ suggest: suggestTurkish,
717
+ render: renderTurkish
718
+ });
719
+ const TurkishLanguage = defineLanguagePlugin({
720
+ id: "chronolizer/language-tr",
721
+ effect: (context) => Effect.asVoid(context.register("chronolizer/language-tr", TurkishContribution))
722
+ });
723
+ const TurkishLanguageLayer = languagePluginsLayer([TurkishLanguage]);
724
+ //#endregion
725
+ export { TurkishContribution, TurkishLanguage, TurkishLanguageLayer };