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,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, 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 parseNamedDate = (input) => {
|
|
302
|
+
const named = String$1.match(/^([0-3]?\d)(?:\.)? ([a-zçğıöşü]+\.?)(?:,)? (\d{4})$/u)(input);
|
|
303
|
+
if (Option.isSome(named)) return namedDatePeriod(textAt(named.value, 3), textAt(named.value, 2), textAt(named.value, 1), monthNumber, dateLabel);
|
|
304
|
+
const numeric = String$1.match(/^([0-3]?\d)[./-]([01]?\d)[./-](\d{4})$/u)(input);
|
|
305
|
+
if (Option.isNone(numeric)) return Option.none();
|
|
306
|
+
const year = validYear(textAt(numeric.value, 3));
|
|
307
|
+
const month = Number(textAt(numeric.value, 2));
|
|
308
|
+
const day = Number(textAt(numeric.value, 1));
|
|
309
|
+
if (year === void 0 || month < 1 || month > 12) return Option.none();
|
|
310
|
+
const value = isoDate(year, month, day);
|
|
311
|
+
return isIsoDate(value) && value !== "9999-12-31" ? Option.some(fixedDatePeriod(value, dateLabel(day, month, year))) : Option.none();
|
|
312
|
+
};
|
|
313
|
+
const quarterNumber = (value) => {
|
|
314
|
+
if ((value.startsWith("q") || value.startsWith("ç")) && value.length === 2) return Number(value.slice(1));
|
|
315
|
+
if (value === "1." || value === "birinci" || value === "ilk") return 1;
|
|
316
|
+
if (value === "2." || value === "ikinci") return 2;
|
|
317
|
+
if (value === "3." || value === "üçüncü" || value === "ucuncu") return 3;
|
|
318
|
+
return value === "4." || value === "dördüncü" || value === "dorduncu" ? 4 : void 0;
|
|
319
|
+
};
|
|
320
|
+
const parseQuarter = (input) => {
|
|
321
|
+
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);
|
|
322
|
+
if (Option.isSome(fixed)) {
|
|
323
|
+
const quarter = quarterNumber(textAt(fixed.value, 1));
|
|
324
|
+
const year = validYear(textAt(fixed.value, 2));
|
|
325
|
+
if (quarter !== void 0 && year !== void 0) return Option.some(fixedQuarterPeriod(year, quarter, `Ç${quarter} ${year}`));
|
|
326
|
+
}
|
|
327
|
+
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);
|
|
328
|
+
if (Option.isSome(relative)) {
|
|
329
|
+
const quarter = quarterNumber(textAt(relative.value, 1));
|
|
330
|
+
const direction = relativeYearDirection(textAt(relative.value, 2));
|
|
331
|
+
if (quarter !== void 0) return Option.some(quarterOfRelativeYear(quarter, direction, `Ç${quarter} ${relativeYearName(direction)}`));
|
|
332
|
+
}
|
|
333
|
+
const standalone = String$1.match(/^(ç[1-4]|q[1-4])$/u)(input);
|
|
334
|
+
if (Option.isNone(standalone)) return Option.none();
|
|
335
|
+
const quarter = quarterNumber(textAt(standalone.value, 1));
|
|
336
|
+
return quarter === void 0 ? Option.none() : Option.some(quarterOfRelativeYear(quarter, 0, `Ç${quarter}`));
|
|
337
|
+
};
|
|
338
|
+
const parseBasePeriod = (input) => {
|
|
339
|
+
if (isIsoDate(input) && input !== "9999-12-31") return Option.some(fixedDatePeriod(input, input));
|
|
340
|
+
const namedDate = parseNamedDate(input);
|
|
341
|
+
if (Option.isSome(namedDate)) return namedDate;
|
|
342
|
+
const quarter = parseQuarter(input);
|
|
343
|
+
if (Option.isSome(quarter)) return quarter;
|
|
344
|
+
const yearMatch = String$1.match(/^(?:yıl |yılı )?(\d{4})$/u)(input);
|
|
345
|
+
if (Option.isSome(yearMatch)) {
|
|
346
|
+
const year = validYear(textAt(yearMatch.value, 1));
|
|
347
|
+
if (year !== void 0) return Option.some(fixedYearPeriod(year, String(year)));
|
|
348
|
+
}
|
|
349
|
+
const monthYear = String$1.match(/^([a-zçğıöşü]+\.?)(?:,)? (\d{4})$/u)(input);
|
|
350
|
+
if (Option.isSome(monthYear)) {
|
|
351
|
+
const month = monthNumber(textAt(monthYear.value, 1));
|
|
352
|
+
const year = validYear(textAt(monthYear.value, 2));
|
|
353
|
+
if (month !== void 0 && year !== void 0) return Option.some(fixedMonthPeriod(year, month, `${title(textAt(months, month - 1))} ${year}`));
|
|
354
|
+
}
|
|
355
|
+
const relativeMonth = String$1.match(/^([a-zçğıöşü]+\.?) (geçen yıl|gelecek yıl|bu yıl)$/u)(input);
|
|
356
|
+
const relativeMonthYearFirst = String$1.match(/^(geçen yıl|gelecek yıl|bu yıl) ([a-zçğıöşü]+\.?)$/u)(input);
|
|
357
|
+
const relativeMatch = Option.firstSomeOf([relativeMonth, relativeMonthYearFirst]);
|
|
358
|
+
if (Option.isSome(relativeMatch)) {
|
|
359
|
+
const yearFirst = Option.isSome(relativeMonthYearFirst);
|
|
360
|
+
const monthText = textAt(relativeMatch.value, yearFirst ? 2 : 1);
|
|
361
|
+
const yearText = textAt(relativeMatch.value, yearFirst ? 1 : 2);
|
|
362
|
+
const month = monthNumber(monthText);
|
|
363
|
+
const direction = relativeYearDirection(yearText);
|
|
364
|
+
if (month !== void 0) return Option.some(monthOfRelativeYear(month, direction, `${title(textAt(months, month - 1))} ${relativeYearName(direction)}`));
|
|
365
|
+
}
|
|
366
|
+
const standaloneMonth = monthNumber(input);
|
|
367
|
+
if (standaloneMonth !== void 0) return Option.some(monthOfRelativeYear(standaloneMonth, 0, title(textAt(months, standaloneMonth - 1))));
|
|
368
|
+
const alias = periodAliases.find((entry) => entry[0] === input);
|
|
369
|
+
if (alias !== void 0) return Option.some(relativePeriod(alias[1], alias[2], alias[3]));
|
|
370
|
+
if (["hafta sonu", "bu hafta sonu"].includes(input)) return Option.some(relativeWeekend(0, "bu hafta sonu"));
|
|
371
|
+
if (["geçen hafta sonu", "önceki hafta sonu"].includes(input)) return Option.some(relativeWeekend(-1, "geçen hafta sonu"));
|
|
372
|
+
if (["gelecek hafta sonu", "önümüzdeki hafta sonu"].includes(input)) return Option.some(relativeWeekend(1, "gelecek hafta sonu"));
|
|
373
|
+
if (input === "geçen hafta sonundan önceki hafta sonu") return Option.some(relativeWeekend(-2, input));
|
|
374
|
+
if (input === "gelecek hafta sonundan sonraki hafta sonu") return Option.some(relativeWeekend(2, input));
|
|
375
|
+
return Option.none();
|
|
376
|
+
};
|
|
377
|
+
const parsePeriod = (input) => {
|
|
378
|
+
const edge = String$1.match(/^(.+?)(?:ın|in|un|ün) (başı|başlangıcı|sonu)$/u)(input);
|
|
379
|
+
if (Option.isSome(edge)) {
|
|
380
|
+
const period = parseBasePeriod(textAt(edge.value, 1));
|
|
381
|
+
if (Option.isSome(period)) {
|
|
382
|
+
const isEnd = textAt(edge.value, 2) === "sonu";
|
|
383
|
+
const canonical = `${period.value.canonical} ${isEnd ? "sonu" : "başı"}`;
|
|
384
|
+
return Option.some(isEnd ? periodEndDay(period.value, canonical) : periodStartDay(period.value, canonical));
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
const wrapper = [
|
|
388
|
+
"boyunca ",
|
|
389
|
+
"içinde ",
|
|
390
|
+
"tüm "
|
|
391
|
+
].find((prefix) => input.startsWith(prefix));
|
|
392
|
+
return parseBasePeriod(wrapper === void 0 ? input : input.slice(wrapper.length));
|
|
393
|
+
};
|
|
394
|
+
const countedUnit = (value) => unitAliases.find((entry) => entry[0] === value)?.[1];
|
|
395
|
+
const countedUnitPattern = "gün|gun|hafta|ay|çeyrek|ceyrek|yıl|yil|sene";
|
|
396
|
+
const countedPattern = (source) => new RegExp(source.replace("UNIT", countedUnitPattern), "u");
|
|
397
|
+
const calendarPastPattern = countedPattern("^([1-9]\\d*) (UNIT) önce$");
|
|
398
|
+
const calendarFuturePatterns = [countedPattern("^([1-9]\\d*) (UNIT) sonra$"), countedPattern("^([1-9]\\d*) (UNIT) içinde$")];
|
|
399
|
+
const rollingPastPatterns = [countedPattern("^(?:son|geçen|önceki|geçtiğimiz) ([1-9]\\d*) (UNIT)$"), countedPattern("^([1-9]\\d*) (?:son|geçen|önceki) (UNIT)$")];
|
|
400
|
+
const rollingFuturePatterns = [countedPattern("^(?:gelecek|önümüzdeki|sonraki) ([1-9]\\d*) (UNIT)$"), countedPattern("^([1-9]\\d*) (?:gelecek|sonraki) (UNIT)$")];
|
|
401
|
+
const rollingSincePattern = countedPattern("^([1-9]\\d*) (UNIT) boyunca$");
|
|
402
|
+
const rollingBarePattern = countedPattern("^([1-9]\\d*) (UNIT)$");
|
|
403
|
+
const firstPatternMatch = (input, patterns) => Option.firstSomeOf(patterns.map((pattern) => String$1.match(pattern)(input)));
|
|
404
|
+
const parseCalendarOffset = (input) => {
|
|
405
|
+
const past = String$1.match(calendarPastPattern)(input);
|
|
406
|
+
const future = firstPatternMatch(input, calendarFuturePatterns);
|
|
407
|
+
const match = Option.firstSomeOf([past, future]);
|
|
408
|
+
if (Option.isNone(match)) return Option.none();
|
|
409
|
+
const amount = parseTrailingCount(textAt(match.value, 1));
|
|
410
|
+
const unit = countedUnit(textAt(match.value, 2));
|
|
411
|
+
if (Option.isNone(amount) || unit === void 0) return Option.none();
|
|
412
|
+
const entry = units.find((item) => item.unit === unit);
|
|
413
|
+
if (entry === void 0) return Option.none();
|
|
414
|
+
const direction = Option.isSome(past) ? -amount.value : amount.value;
|
|
415
|
+
const noun = amount.value === 1 ? entry.singular : entry.plural;
|
|
416
|
+
const canonical = direction < 0 ? `${amount.value} ${noun} önce` : `${amount.value} ${noun} sonra`;
|
|
417
|
+
return Option.some(candidate(periodRange(relativePeriod(unit, direction, canonical)), canonical));
|
|
418
|
+
};
|
|
419
|
+
const parseRollingPeriod = (input) => {
|
|
420
|
+
const since = String$1.match(rollingSincePattern)(input);
|
|
421
|
+
const past = firstPatternMatch(input, rollingPastPatterns);
|
|
422
|
+
const bare = String$1.match(rollingBarePattern)(input);
|
|
423
|
+
const future = firstPatternMatch(input, rollingFuturePatterns);
|
|
424
|
+
const match = Option.firstSomeOf([
|
|
425
|
+
since,
|
|
426
|
+
past,
|
|
427
|
+
bare,
|
|
428
|
+
future
|
|
429
|
+
]);
|
|
430
|
+
if (Option.isNone(match)) return Option.none();
|
|
431
|
+
const amount = parseTrailingCount(textAt(match.value, 1));
|
|
432
|
+
const unit = countedUnit(textAt(match.value, 2));
|
|
433
|
+
if (Option.isNone(amount) || unit === void 0) return Option.none();
|
|
434
|
+
const entry = units.find((item) => item.unit === unit);
|
|
435
|
+
if (entry === void 0) return Option.none();
|
|
436
|
+
const isFuture = Option.isSome(future);
|
|
437
|
+
const range = isFuture ? futureRange(amount.value, unit) : trailingRange(amount.value, unit);
|
|
438
|
+
const modifier = isFuture ? "gelecek" : "son";
|
|
439
|
+
const noun = amount.value === 1 ? entry.singular : entry.plural;
|
|
440
|
+
return Option.some(candidate(range, `${modifier} ${amount.value} ${noun}`));
|
|
441
|
+
};
|
|
442
|
+
const suffixBoundary = (input) => {
|
|
443
|
+
const boundaries = [
|
|
444
|
+
[String$1.match(/^(.+?)'?(?:den|dan|ten|tan) itibaren$/u)(input), "since"],
|
|
445
|
+
[String$1.match(/^(.+) itibarıyla$/u)(input), "since"],
|
|
446
|
+
[String$1.match(/^(.+?)'?(?:den|dan|ten|tan) önce$/u)(input), "before"],
|
|
447
|
+
[String$1.match(/^(.+) öncesi$/u)(input), "before"],
|
|
448
|
+
[String$1.match(/^(.+?)'?(?:e|a|ye|ya) kadar$/u)(input), "through"],
|
|
449
|
+
[String$1.match(/^(.+) sonuna kadar$/u)(input), "through"],
|
|
450
|
+
[String$1.match(/^(.+?)'?(?:den|dan|ten|tan) sonra$/u)(input), "after"],
|
|
451
|
+
[String$1.match(/^(.+) sonrası$/u)(input), "after"]
|
|
452
|
+
];
|
|
453
|
+
for (const [match, boundary] of boundaries) {
|
|
454
|
+
if (Option.isNone(match)) continue;
|
|
455
|
+
const period = parsePeriod(textAt(match.value, 1));
|
|
456
|
+
if (Option.isSome(period)) return Option.some(periodBoundaryCandidate(period.value, boundary, input));
|
|
457
|
+
}
|
|
458
|
+
return Option.none();
|
|
459
|
+
};
|
|
460
|
+
const boundaryCandidate = (input) => {
|
|
461
|
+
const suffix = suffixBoundary(input);
|
|
462
|
+
if (Option.isSome(suffix)) return suffix;
|
|
463
|
+
return openBoundaryCandidate(input, [
|
|
464
|
+
["itibaren ", "since"],
|
|
465
|
+
["önce ", "before"],
|
|
466
|
+
["kadar ", "through"],
|
|
467
|
+
["sonra ", "after"]
|
|
468
|
+
], parsePeriod);
|
|
469
|
+
};
|
|
470
|
+
const parseTurkish = (input) => {
|
|
471
|
+
const remaining = remainingPhrases.find((entry) => entry.phrase === input);
|
|
472
|
+
if (remaining !== void 0) return Option.some(candidate(remainingPeriodRange(remaining.entry.unit), remaining.entry.remaining));
|
|
473
|
+
if ([
|
|
474
|
+
"bugüne kadar",
|
|
475
|
+
"şimdiye kadar",
|
|
476
|
+
"şu ana kadar"
|
|
477
|
+
].includes(input)) return Option.some(candidate(untilNowRange(), "şimdiye kadar"));
|
|
478
|
+
if ([
|
|
479
|
+
"bugünden itibaren",
|
|
480
|
+
"şu andan itibaren",
|
|
481
|
+
"bundan sonra"
|
|
482
|
+
].includes(input)) return Option.some(candidate(fromNowRange(), "bugünden itibaren"));
|
|
483
|
+
const offset = parseCalendarOffset(input);
|
|
484
|
+
if (Option.isSome(offset)) return offset;
|
|
485
|
+
const rolling = parseRollingPeriod(input);
|
|
486
|
+
if (Option.isSome(rolling)) return rolling;
|
|
487
|
+
const toDate = toDatePhrases.find((entry) => entry.phrase === input);
|
|
488
|
+
if (toDate !== void 0) return Option.some(candidate(periodToDateRange(toDate.entry.unit), toDate.entry.toDate));
|
|
489
|
+
const joinedInput = input.endsWith(" arası") ? input.slice(0, -6) : input;
|
|
490
|
+
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ı`);
|
|
491
|
+
if (Option.isSome(nowBounded)) return nowBounded;
|
|
492
|
+
const bounded = joinedPeriodCandidate(joinedInput, [
|
|
493
|
+
["", " ile "],
|
|
494
|
+
["", " ve "],
|
|
495
|
+
["", " - "],
|
|
496
|
+
["", " – "],
|
|
497
|
+
["", " — "]
|
|
498
|
+
], parsePeriod, (lower, upper) => `${lower} ile ${upper} arası`);
|
|
499
|
+
if (Option.isSome(bounded)) return bounded;
|
|
500
|
+
const boundary = boundaryCandidate(input);
|
|
501
|
+
if (Option.isSome(boundary)) return boundary;
|
|
502
|
+
return Option.map(parsePeriod(input), (period) => candidate(periodRange(period), period.canonical));
|
|
503
|
+
};
|
|
504
|
+
const staticPeriodPhrases = [
|
|
505
|
+
...periodAliases.map((entry) => entry[0]),
|
|
506
|
+
"bu hafta sonu",
|
|
507
|
+
"geçen hafta sonu",
|
|
508
|
+
"gelecek hafta sonu",
|
|
509
|
+
"geçen hafta sonundan önceki hafta sonu",
|
|
510
|
+
"gelecek hafta sonundan sonraki hafta sonu",
|
|
511
|
+
"bu haftanın başı",
|
|
512
|
+
"bu haftanın sonu",
|
|
513
|
+
"geçen haftanın başı",
|
|
514
|
+
"geçen haftanın sonu",
|
|
515
|
+
"gelecek haftanın başı",
|
|
516
|
+
"gelecek haftanın sonu",
|
|
517
|
+
"bu ayın başı",
|
|
518
|
+
"bu ayın sonu",
|
|
519
|
+
"geçen ayın başı",
|
|
520
|
+
"geçen ayın sonu",
|
|
521
|
+
"gelecek ayın başı",
|
|
522
|
+
"gelecek ayın sonu",
|
|
523
|
+
"bu yılın başı",
|
|
524
|
+
"bu yılın sonu",
|
|
525
|
+
"geçen yılın başı",
|
|
526
|
+
"geçen yılın sonu",
|
|
527
|
+
"gelecek yılın başı",
|
|
528
|
+
"gelecek yılın sonu",
|
|
529
|
+
...[
|
|
530
|
+
1,
|
|
531
|
+
2,
|
|
532
|
+
3,
|
|
533
|
+
4
|
|
534
|
+
].flatMap((quarter) => [
|
|
535
|
+
`ç${quarter}`,
|
|
536
|
+
`ç${quarter} bu yıl`,
|
|
537
|
+
`ç${quarter} geçen yıl`,
|
|
538
|
+
`ç${quarter} gelecek yıl`
|
|
539
|
+
]),
|
|
540
|
+
...months.flatMap((month) => [
|
|
541
|
+
month,
|
|
542
|
+
`${month} geçen yıl`,
|
|
543
|
+
`${month} gelecek yıl`
|
|
544
|
+
])
|
|
545
|
+
];
|
|
546
|
+
const staticPeriods = periodsFromPhrases(staticPeriodPhrases, parsePeriod);
|
|
547
|
+
const boundaryPrefixes = [
|
|
548
|
+
"itibaren ",
|
|
549
|
+
"önce ",
|
|
550
|
+
"kadar ",
|
|
551
|
+
"sonra "
|
|
552
|
+
];
|
|
553
|
+
const countedSuggestions = (input) => {
|
|
554
|
+
const amount = naturalCount(input);
|
|
555
|
+
if (amount === void 0) return [];
|
|
556
|
+
return units.flatMap((entry) => {
|
|
557
|
+
const noun = amount === 1 ? entry.singular : entry.plural;
|
|
558
|
+
return [
|
|
559
|
+
`son ${amount} ${noun}`,
|
|
560
|
+
`geçen ${amount} ${noun}`,
|
|
561
|
+
`${amount} ${noun}`,
|
|
562
|
+
`gelecek ${amount} ${noun}`,
|
|
563
|
+
`önümüzdeki ${amount} ${noun}`,
|
|
564
|
+
`${amount} ${noun} önce`,
|
|
565
|
+
`${amount} ${noun} sonra`
|
|
566
|
+
];
|
|
567
|
+
});
|
|
568
|
+
};
|
|
569
|
+
const turkishSuggestionPhrases = [
|
|
570
|
+
...units.map((entry) => entry.toDate),
|
|
571
|
+
...units.map((entry) => entry.remaining),
|
|
572
|
+
...staticPeriodPhrases,
|
|
573
|
+
...prefixNaturalPhrases(staticPeriodPhrases, boundaryPrefixes),
|
|
574
|
+
"şimdiye kadar",
|
|
575
|
+
"bugünden itibaren"
|
|
576
|
+
];
|
|
577
|
+
const suggestTurkish = (input, limit) => {
|
|
578
|
+
const fixed = fixedCalendarPeriodPhrases(input, months).map((phrase) => phrase.startsWith("q") ? `ç${phrase.slice(1)}` : phrase);
|
|
579
|
+
return completeNaturalPhrases(input, [
|
|
580
|
+
...turkishSuggestionPhrases,
|
|
581
|
+
...fixed,
|
|
582
|
+
...prefixNaturalPhrases(fixed, boundaryPrefixes),
|
|
583
|
+
...countedSuggestions(input)
|
|
584
|
+
], limit);
|
|
585
|
+
};
|
|
586
|
+
const renderTurkish = (range) => {
|
|
587
|
+
const offset = calendarPeriodOffset(range);
|
|
588
|
+
if (Option.isSome(offset) && Math.abs(offset.value.amount) > 1) {
|
|
589
|
+
const entry = units.find((unit) => unit.unit === offset.value.unit);
|
|
590
|
+
if (entry !== void 0) {
|
|
591
|
+
const amount = Math.abs(offset.value.amount);
|
|
592
|
+
const noun = amount === 1 ? entry.singular : entry.plural;
|
|
593
|
+
return Option.some(offset.value.amount < 0 ? `${amount} ${noun} önce` : `${amount} ${noun} sonra`);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
const future = futurePeriod(range);
|
|
597
|
+
if (Option.isSome(future)) {
|
|
598
|
+
const entry = units.find((unit) => unit.unit === future.value.unit);
|
|
599
|
+
if (entry !== void 0) {
|
|
600
|
+
const noun = future.value.amount === 1 ? entry.singular : entry.plural;
|
|
601
|
+
return Option.some(`gelecek ${future.value.amount} ${noun}`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
const trailing = trailingPeriod(range);
|
|
605
|
+
if (Option.isSome(trailing)) {
|
|
606
|
+
const entry = units.find((unit) => unit.unit === trailing.value.unit);
|
|
607
|
+
if (entry !== void 0) {
|
|
608
|
+
const noun = trailing.value.amount === 1 ? entry.singular : entry.plural;
|
|
609
|
+
return Option.some(`son ${trailing.value.amount} ${noun}`);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
const periods = [...staticPeriods, ...periodsFromPhrases([...datedPeriods(range, months), ...datedQuarterPeriods(range).map((phrase) => phrase.startsWith("Q") ? `Ç${phrase.slice(1)}` : phrase)], parsePeriod)];
|
|
613
|
+
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");
|
|
614
|
+
};
|
|
615
|
+
const normalizeTurkish = (input, locale) => normalizeNaturalText(input, locale).replaceAll("’", "'");
|
|
616
|
+
const TurkishContribution = new BaseLanguageContribution({
|
|
617
|
+
locale: "tr",
|
|
618
|
+
vocabulary: [
|
|
619
|
+
...months,
|
|
620
|
+
...monthAbbreviations.flatMap((aliases) => aliases),
|
|
621
|
+
...units.flatMap((entry) => [
|
|
622
|
+
entry.singular,
|
|
623
|
+
entry.plural,
|
|
624
|
+
...entry.current.split(" "),
|
|
625
|
+
...entry.previous.split(" "),
|
|
626
|
+
...entry.next.split(" ")
|
|
627
|
+
]),
|
|
628
|
+
...toDatePhrases.flatMap((entry) => entry.phrase.split(" ")),
|
|
629
|
+
...remainingPhrases.flatMap((entry) => entry.phrase.split(" ")),
|
|
630
|
+
"arası",
|
|
631
|
+
"başı",
|
|
632
|
+
"bugün",
|
|
633
|
+
"gelecek",
|
|
634
|
+
"geçen",
|
|
635
|
+
"geri",
|
|
636
|
+
"itibaren",
|
|
637
|
+
"itibarıyla",
|
|
638
|
+
"kadar",
|
|
639
|
+
"kalanı",
|
|
640
|
+
"önce",
|
|
641
|
+
"önceki",
|
|
642
|
+
"önümüzdeki",
|
|
643
|
+
"son",
|
|
644
|
+
"sonra",
|
|
645
|
+
"sonrası",
|
|
646
|
+
"şimdiye"
|
|
647
|
+
],
|
|
648
|
+
normalize: normalizeTurkish,
|
|
649
|
+
correct: correctWhitespaceSeparatedText,
|
|
650
|
+
parseExact: parseTurkish,
|
|
651
|
+
suggest: suggestTurkish,
|
|
652
|
+
render: renderTurkish
|
|
653
|
+
});
|
|
654
|
+
const TurkishLanguage = defineLanguagePlugin({
|
|
655
|
+
id: "chronolizer/language-tr",
|
|
656
|
+
effect: (context) => Effect.asVoid(context.register("chronolizer/language-tr", TurkishContribution))
|
|
657
|
+
});
|
|
658
|
+
const TurkishLanguageLayer = languagePluginsLayer([TurkishLanguage]);
|
|
659
|
+
//#endregion
|
|
660
|
+
export { TurkishContribution, TurkishLanguage, TurkishLanguageLayer };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//#region src/natural/correction.d.ts
|
|
2
|
+
declare const correctWhitespaceSeparatedText: (input: string, vocabulary: ReadonlyArray<string>) => {
|
|
3
|
+
readonly text: string;
|
|
4
|
+
readonly corrections: readonly {
|
|
5
|
+
readonly original: string;
|
|
6
|
+
readonly replacement: string;
|
|
7
|
+
readonly distance: number;
|
|
8
|
+
readonly offset: number;
|
|
9
|
+
}[];
|
|
10
|
+
readonly cost: number;
|
|
11
|
+
}[];
|
|
12
|
+
//#endregion
|
|
13
|
+
export { correctWhitespaceSeparatedText };
|