chronolizer 0.2.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.
- package/README.md +169 -167
- package/dist/ast/constructors.d.mts +3 -8
- package/dist/ast/fold.d.mts +2 -1
- package/dist/ast/fold.mjs +38 -31
- package/dist/ast/normalize.mjs +4 -2
- package/dist/ast/schemas.d.mts +18 -25
- package/dist/ast/schemas.mjs +8 -17
- package/dist/filter/expression.mjs +3 -20
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/language/model.d.mts +33 -33
- package/dist/language/model.mjs +4 -7
- package/dist/language/registry.mjs +3 -21
- package/dist/locales/cs.mjs +70 -17
- package/dist/locales/de.mjs +237 -26
- package/dist/locales/en.mjs +69 -21
- package/dist/locales/es.mjs +325 -77
- package/dist/locales/fr.mjs +215 -51
- package/dist/locales/nl.mjs +189 -50
- package/dist/locales/pl.mjs +295 -121
- package/dist/locales/shared.mjs +129 -23
- package/dist/locales/tr.mjs +86 -21
- package/dist/natural/parse.d.mts +1 -1
- package/dist/natural/policy.mjs +2 -2
- package/dist/resolve/resolve.mjs +1 -9
- package/package.json +1 -1
package/dist/ast/schemas.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Option, Schema, String } from "effect";
|
|
1
|
+
import { Match, Option, Schema, String } from "effect";
|
|
2
2
|
//#region src/ast/schemas.ts
|
|
3
3
|
const Unit = Schema.Literals([
|
|
4
4
|
"day",
|
|
@@ -8,16 +8,7 @@ const Unit = Schema.Literals([
|
|
|
8
8
|
"year"
|
|
9
9
|
]);
|
|
10
10
|
const isLeapYear = (year) => year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
11
|
-
const daysInMonth = (year, month) =>
|
|
12
|
-
switch (month) {
|
|
13
|
-
case 2: return isLeapYear(year) ? 29 : 28;
|
|
14
|
-
case 4:
|
|
15
|
-
case 6:
|
|
16
|
-
case 9:
|
|
17
|
-
case 11: return 30;
|
|
18
|
-
default: return 31;
|
|
19
|
-
}
|
|
20
|
-
};
|
|
11
|
+
const daysInMonth = (year, month) => Match.value(month).pipe(Match.when(2, () => isLeapYear(year) ? 29 : 28), Match.when(Match.is(4, 6, 9, 11), () => 30), Match.orElse(() => 31));
|
|
21
12
|
const isIsoDate = (value) => {
|
|
22
13
|
if (Option.isNone(String.match(/^\d{4}-\d{2}-\d{2}$/)(value))) return false;
|
|
23
14
|
const year = Number(value.slice(0, 4));
|
|
@@ -26,21 +17,21 @@ const isIsoDate = (value) => {
|
|
|
26
17
|
return month >= 1 && month <= 12 && day >= 1 && day <= daysInMonth(year, month);
|
|
27
18
|
};
|
|
28
19
|
const IsoDate = Schema.String.check(Schema.makeFilter(isIsoDate, { expected: "an ISO calendar date (YYYY-MM-DD)" })).annotate({ identifier: "IsoDate" });
|
|
29
|
-
|
|
30
|
-
|
|
20
|
+
var Now = class extends Schema.TaggedClass()("Now", {}) {};
|
|
21
|
+
var DateLiteral = class extends Schema.TaggedClass()("DateLiteral", { value: IsoDate }) {};
|
|
31
22
|
const ShiftAmount = Schema.Int.check(Schema.isBetween({
|
|
32
23
|
minimum: Number.MIN_SAFE_INTEGER,
|
|
33
24
|
maximum: Number.MAX_SAFE_INTEGER
|
|
34
25
|
}));
|
|
35
|
-
|
|
26
|
+
var Shift = class extends Schema.TaggedClass()("Shift", {
|
|
36
27
|
base: Schema.suspend(() => InstantExpr),
|
|
37
28
|
amount: ShiftAmount,
|
|
38
29
|
unit: Unit
|
|
39
|
-
});
|
|
40
|
-
|
|
30
|
+
}) {};
|
|
31
|
+
var StartOf = class extends Schema.TaggedClass()("StartOf", {
|
|
41
32
|
base: Schema.suspend(() => InstantExpr),
|
|
42
33
|
unit: Unit
|
|
43
|
-
});
|
|
34
|
+
}) {};
|
|
44
35
|
const InstantExpr = Schema.suspend(() => Schema.Union([
|
|
45
36
|
Now,
|
|
46
37
|
DateLiteral,
|
|
@@ -3,27 +3,10 @@ import { dateLiteral, now, shift, startOf } from "../ast/constructors.mjs";
|
|
|
3
3
|
import { foldInstant } from "../ast/fold.mjs";
|
|
4
4
|
import { normalizeInstant } from "../ast/normalize.mjs";
|
|
5
5
|
import { FilterExpressionParseError } from "./errors.mjs";
|
|
6
|
-
import { Effect } from "effect";
|
|
6
|
+
import { Effect, Match } from "effect";
|
|
7
7
|
//#region src/filter/expression.ts
|
|
8
|
-
const unitFromSymbol = (symbol) =>
|
|
9
|
-
|
|
10
|
-
case "d": return "day";
|
|
11
|
-
case "w": return "week";
|
|
12
|
-
case "M": return "month";
|
|
13
|
-
case "q": return "quarter";
|
|
14
|
-
case "y": return "year";
|
|
15
|
-
default: return;
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
const symbolFromUnit = (unit) => {
|
|
19
|
-
switch (unit) {
|
|
20
|
-
case "day": return "d";
|
|
21
|
-
case "week": return "w";
|
|
22
|
-
case "month": return "M";
|
|
23
|
-
case "quarter": return "q";
|
|
24
|
-
case "year": return "y";
|
|
25
|
-
}
|
|
26
|
-
};
|
|
8
|
+
const unitFromSymbol = (symbol) => Match.value(symbol).pipe(Match.when("d", () => "day"), Match.when("w", () => "week"), Match.when("M", () => "month"), Match.when("q", () => "quarter"), Match.when("y", () => "year"), Match.orElse(() => void 0));
|
|
9
|
+
const symbolFromUnit = (unit) => Match.value(unit).pipe(Match.when("day", () => "d"), Match.when("week", () => "w"), Match.when("month", () => "M"), Match.when("quarter", () => "q"), Match.when("year", () => "y"), Match.exhaustive);
|
|
27
10
|
const failAt = (input, offset, expected) => Effect.fail(new FilterExpressionParseError({
|
|
28
11
|
input,
|
|
29
12
|
offset,
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DateLiteral, DateRangeExpr, GreaterThan, GreaterThanOrEqual, InstantExpr, IsoDate, LessThan, LessThanOrEqual, LowerBound, Now, Shift, StartOf, Unit, UpperBound, daysInMonth, isIsoDate } from "./ast/schemas.mjs";
|
|
2
2
|
import { boundedRange, dateLiteral, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, lowerOpenRange, now, shift, startOf, upperOpenRange } from "./ast/constructors.mjs";
|
|
3
|
-
import { InstantAlgebra, containsPositiveShift, foldInstant } from "./ast/fold.mjs";
|
|
3
|
+
import { InstantAlgebra, containsPositiveShift, foldInstant, isCurrentPeriod } from "./ast/fold.mjs";
|
|
4
4
|
import { normalizeInstant, normalizeRange } from "./ast/normalize.mjs";
|
|
5
5
|
import { FilterExpressionParseError, InvalidDateFilterError } from "./filter/errors.mjs";
|
|
6
6
|
import { completePeriod, formatFilter, parseFilter, rangeKey } from "./filter/codec.mjs";
|
|
@@ -19,4 +19,4 @@ import { SuggestNaturalOptions, suggestNatural } from "./natural/suggest.mjs";
|
|
|
19
19
|
import { naturalWords, normalizeNaturalText } from "./natural/text.mjs";
|
|
20
20
|
import { ResolutionError, ResolvedDateRange, ResolvedGreaterThan, ResolvedGreaterThanOrEqual, ResolvedLessThan, ResolvedLessThanOrEqual, ResolvedLowerBound, ResolvedUpperBound } from "./resolve/schema.mjs";
|
|
21
21
|
import { resolve } from "./resolve/resolve.mjs";
|
|
22
|
-
export { AmbiguousNaturalLanguageError, BaseLanguageContribution, BaseLanguageMetadata, CompiledLanguage, Correction, DateExpressionString, DateFilter, DateLiteral, DateRangeExpr, DateRangeFromFilter, EnglishContribution, EnglishLanguage, EnglishLanguageLayer, FilterExpressionParseError, type FormatNaturalOptions, GreaterThan, GreaterThanOrEqual, InstantAlgebra, InstantExpr, InstantExpressionFromString, InvalidDateFilterError, IsoDate, LanguageConflictError, LanguageContribution, LanguageContributionMetadata, LanguageExtensionContribution, LanguageExtensionMetadata, LanguagePlugin, LanguagePluginContext, LanguageRegistrationError, LanguageRegistry, LanguageRegistryLayer, LessThan, LessThanOrEqual, Locale, LowerBound, NaturalAlternative, NaturalCandidate, NaturalCorrectionCandidate, NaturalLanguageParseError, NaturalLanguageRenderError, NaturalParseResult, NaturalSuggestion, Now, type ParseNaturalOptions, ParseQuality, ResolutionError, ResolvedDateRange, ResolvedGreaterThan, ResolvedGreaterThanOrEqual, ResolvedLessThan, ResolvedLessThanOrEqual, ResolvedLowerBound, ResolvedUpperBound, Shift, StartOf, type SuggestNaturalOptions, Unit, UnsupportedLocaleError, UpperBound, boundedRange, canonicalBaseLocale, completeNaturalPhrases, completePeriod, containsPositiveShift, correctWhitespaceSeparatedText, dateLiteral, daysInMonth, defineLanguagePlugin, foldInstant, formatFilter, formatInstantExpression, formatNatural, greaterThan, greaterThanOrEqual, isIsoDate, languagePluginsLayer, lessThan, lessThanOrEqual, lowerOpenRange, naturalWords, normalizeInstant, normalizeNaturalText, normalizeRange, now, parseFilter, parseInstantExpression, parseNatural, rangeKey, resolve, shift, startOf, suggestNatural, upperOpenRange };
|
|
22
|
+
export { AmbiguousNaturalLanguageError, BaseLanguageContribution, BaseLanguageMetadata, CompiledLanguage, Correction, DateExpressionString, DateFilter, DateLiteral, DateRangeExpr, DateRangeFromFilter, EnglishContribution, EnglishLanguage, EnglishLanguageLayer, FilterExpressionParseError, type FormatNaturalOptions, GreaterThan, GreaterThanOrEqual, InstantAlgebra, InstantExpr, InstantExpressionFromString, InvalidDateFilterError, IsoDate, LanguageConflictError, LanguageContribution, LanguageContributionMetadata, LanguageExtensionContribution, LanguageExtensionMetadata, LanguagePlugin, LanguagePluginContext, LanguageRegistrationError, LanguageRegistry, LanguageRegistryLayer, LessThan, LessThanOrEqual, Locale, LowerBound, NaturalAlternative, NaturalCandidate, NaturalCorrectionCandidate, NaturalLanguageParseError, NaturalLanguageRenderError, NaturalParseResult, NaturalSuggestion, Now, type ParseNaturalOptions, ParseQuality, ResolutionError, ResolvedDateRange, ResolvedGreaterThan, ResolvedGreaterThanOrEqual, ResolvedLessThan, ResolvedLessThanOrEqual, ResolvedLowerBound, ResolvedUpperBound, Shift, StartOf, type SuggestNaturalOptions, Unit, UnsupportedLocaleError, UpperBound, boundedRange, canonicalBaseLocale, completeNaturalPhrases, completePeriod, containsPositiveShift, correctWhitespaceSeparatedText, dateLiteral, daysInMonth, defineLanguagePlugin, foldInstant, formatFilter, formatInstantExpression, formatNatural, greaterThan, greaterThanOrEqual, isCurrentPeriod, isIsoDate, languagePluginsLayer, lessThan, lessThanOrEqual, lowerOpenRange, naturalWords, normalizeInstant, normalizeNaturalText, normalizeRange, now, parseFilter, parseInstantExpression, parseNatural, rangeKey, resolve, shift, startOf, suggestNatural, upperOpenRange };
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DateLiteral, DateRangeExpr, GreaterThan, GreaterThanOrEqual, InstantExpr, IsoDate, LessThan, LessThanOrEqual, LowerBound, Now, Shift, StartOf, Unit, UpperBound, daysInMonth, isIsoDate } from "./ast/schemas.mjs";
|
|
2
2
|
import { boundedRange, dateLiteral, greaterThan, greaterThanOrEqual, lessThan, lessThanOrEqual, lowerOpenRange, now, shift, startOf, upperOpenRange } from "./ast/constructors.mjs";
|
|
3
|
-
import { containsPositiveShift, foldInstant } from "./ast/fold.mjs";
|
|
3
|
+
import { containsPositiveShift, foldInstant, isCurrentPeriod } from "./ast/fold.mjs";
|
|
4
4
|
import { normalizeInstant, normalizeRange } from "./ast/normalize.mjs";
|
|
5
5
|
import { FilterExpressionParseError, InvalidDateFilterError } from "./filter/errors.mjs";
|
|
6
6
|
import { formatInstantExpression, parseInstantExpression } from "./filter/expression.mjs";
|
|
@@ -19,4 +19,4 @@ import { parseNatural } from "./natural/parse.mjs";
|
|
|
19
19
|
import { suggestNatural } from "./natural/suggest.mjs";
|
|
20
20
|
import { ResolutionError, ResolvedDateRange, ResolvedGreaterThan, ResolvedGreaterThanOrEqual, ResolvedLessThan, ResolvedLessThanOrEqual, ResolvedLowerBound, ResolvedUpperBound } from "./resolve/schema.mjs";
|
|
21
21
|
import { resolve } from "./resolve/resolve.mjs";
|
|
22
|
-
export { AmbiguousNaturalLanguageError, BaseLanguageContribution, BaseLanguageMetadata, Correction, DateExpressionString, DateFilter, DateLiteral, DateRangeExpr, DateRangeFromFilter, EnglishContribution, EnglishLanguage, EnglishLanguageLayer, FilterExpressionParseError, GreaterThan, GreaterThanOrEqual, InstantExpr, InstantExpressionFromString, InvalidDateFilterError, IsoDate, LanguageConflictError, LanguageContributionMetadata, LanguageExtensionContribution, LanguageExtensionMetadata, LanguageRegistrationError, LanguageRegistry, LanguageRegistryLayer, LessThan, LessThanOrEqual, Locale, LowerBound, NaturalAlternative, NaturalCandidate, NaturalCorrectionCandidate, NaturalLanguageParseError, NaturalLanguageRenderError, NaturalParseResult, NaturalSuggestion, Now, ParseQuality, ResolutionError, ResolvedDateRange, ResolvedGreaterThan, ResolvedGreaterThanOrEqual, ResolvedLessThan, ResolvedLessThanOrEqual, ResolvedLowerBound, ResolvedUpperBound, Shift, StartOf, Unit, UnsupportedLocaleError, UpperBound, boundedRange, canonicalBaseLocale, completeNaturalPhrases, completePeriod, containsPositiveShift, correctWhitespaceSeparatedText, dateLiteral, daysInMonth, defineLanguagePlugin, foldInstant, formatFilter, formatInstantExpression, formatNatural, greaterThan, greaterThanOrEqual, isIsoDate, languagePluginsLayer, lessThan, lessThanOrEqual, lowerOpenRange, naturalWords, normalizeInstant, normalizeNaturalText, normalizeRange, now, parseFilter, parseInstantExpression, parseNatural, rangeKey, resolve, shift, startOf, suggestNatural, upperOpenRange };
|
|
22
|
+
export { AmbiguousNaturalLanguageError, BaseLanguageContribution, BaseLanguageMetadata, Correction, DateExpressionString, DateFilter, DateLiteral, DateRangeExpr, DateRangeFromFilter, EnglishContribution, EnglishLanguage, EnglishLanguageLayer, FilterExpressionParseError, GreaterThan, GreaterThanOrEqual, InstantExpr, InstantExpressionFromString, InvalidDateFilterError, IsoDate, LanguageConflictError, LanguageContributionMetadata, LanguageExtensionContribution, LanguageExtensionMetadata, LanguageRegistrationError, LanguageRegistry, LanguageRegistryLayer, LessThan, LessThanOrEqual, Locale, LowerBound, NaturalAlternative, NaturalCandidate, NaturalCorrectionCandidate, NaturalLanguageParseError, NaturalLanguageRenderError, NaturalParseResult, NaturalSuggestion, Now, ParseQuality, ResolutionError, ResolvedDateRange, ResolvedGreaterThan, ResolvedGreaterThanOrEqual, ResolvedLessThan, ResolvedLessThanOrEqual, ResolvedLowerBound, ResolvedUpperBound, Shift, StartOf, Unit, UnsupportedLocaleError, UpperBound, boundedRange, canonicalBaseLocale, completeNaturalPhrases, completePeriod, containsPositiveShift, correctWhitespaceSeparatedText, dateLiteral, daysInMonth, defineLanguagePlugin, foldInstant, formatFilter, formatInstantExpression, formatNatural, greaterThan, greaterThanOrEqual, isCurrentPeriod, isIsoDate, languagePluginsLayer, lessThan, lessThanOrEqual, lowerOpenRange, naturalWords, normalizeInstant, normalizeNaturalText, normalizeRange, now, parseFilter, parseInstantExpression, parseNatural, rangeKey, resolve, shift, startOf, suggestNatural, upperOpenRange };
|
|
@@ -22,8 +22,37 @@ declare const NaturalCorrectionCandidate: Schema.Struct<{
|
|
|
22
22
|
readonly cost: Schema.Int;
|
|
23
23
|
}>;
|
|
24
24
|
type NaturalCorrectionCandidate = typeof NaturalCorrectionCandidate.Type;
|
|
25
|
-
declare const
|
|
25
|
+
declare const NaturalCandidate: Schema.Struct<{
|
|
26
|
+
readonly range: Schema.Union<readonly [Schema.TaggedStruct<"DateRange", {
|
|
27
|
+
readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
|
|
28
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
29
|
+
}>, Schema.TaggedStruct<"GreaterThanOrEqual", {
|
|
30
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
31
|
+
}>]>;
|
|
32
|
+
readonly upper: Schema.Union<readonly [Schema.TaggedStruct<"LessThan", {
|
|
33
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
34
|
+
}>, Schema.TaggedStruct<"LessThanOrEqual", {
|
|
35
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
36
|
+
}>]>;
|
|
37
|
+
}>, Schema.TaggedStruct<"DateRange", {
|
|
38
|
+
readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
|
|
39
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
40
|
+
}>, Schema.TaggedStruct<"GreaterThanOrEqual", {
|
|
41
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
42
|
+
}>]>;
|
|
43
|
+
readonly upper: Schema.optionalKey<Schema.Never>;
|
|
44
|
+
}>, Schema.TaggedStruct<"DateRange", {
|
|
45
|
+
readonly lower: Schema.optionalKey<Schema.Never>;
|
|
46
|
+
readonly upper: Schema.Union<readonly [Schema.TaggedStruct<"LessThan", {
|
|
47
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
48
|
+
}>, Schema.TaggedStruct<"LessThanOrEqual", {
|
|
49
|
+
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
50
|
+
}>]>;
|
|
51
|
+
}>]>;
|
|
26
52
|
readonly canonical: Schema.String;
|
|
53
|
+
}>;
|
|
54
|
+
type NaturalCandidate = typeof NaturalCandidate.Type;
|
|
55
|
+
declare const NaturalAlternative: Schema.Struct<{
|
|
27
56
|
readonly range: Schema.Union<readonly [Schema.TaggedStruct<"DateRange", {
|
|
28
57
|
readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
|
|
29
58
|
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
@@ -50,8 +79,9 @@ declare const NaturalAlternative: Schema.Struct<{
|
|
|
50
79
|
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
51
80
|
}>]>;
|
|
52
81
|
}>]>;
|
|
82
|
+
readonly canonical: Schema.String;
|
|
53
83
|
}>;
|
|
54
|
-
type NaturalAlternative =
|
|
84
|
+
type NaturalAlternative = NaturalCandidate;
|
|
55
85
|
declare const NaturalParseResult: Schema.Struct<{
|
|
56
86
|
readonly range: Schema.Union<readonly [Schema.TaggedStruct<"DateRange", {
|
|
57
87
|
readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
|
|
@@ -87,7 +117,6 @@ declare const NaturalParseResult: Schema.Struct<{
|
|
|
87
117
|
readonly offset: Schema.Int;
|
|
88
118
|
}>>;
|
|
89
119
|
readonly alternatives: Schema.$Array<Schema.Struct<{
|
|
90
|
-
readonly canonical: Schema.String;
|
|
91
120
|
readonly range: Schema.Union<readonly [Schema.TaggedStruct<"DateRange", {
|
|
92
121
|
readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
|
|
93
122
|
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
@@ -114,39 +143,10 @@ declare const NaturalParseResult: Schema.Struct<{
|
|
|
114
143
|
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
115
144
|
}>]>;
|
|
116
145
|
}>]>;
|
|
146
|
+
readonly canonical: Schema.String;
|
|
117
147
|
}>>;
|
|
118
148
|
}>;
|
|
119
149
|
type NaturalParseResult = typeof NaturalParseResult.Type;
|
|
120
|
-
declare const NaturalCandidate: Schema.Struct<{
|
|
121
|
-
readonly range: Schema.Union<readonly [Schema.TaggedStruct<"DateRange", {
|
|
122
|
-
readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
|
|
123
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
124
|
-
}>, Schema.TaggedStruct<"GreaterThanOrEqual", {
|
|
125
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
126
|
-
}>]>;
|
|
127
|
-
readonly upper: Schema.Union<readonly [Schema.TaggedStruct<"LessThan", {
|
|
128
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
129
|
-
}>, Schema.TaggedStruct<"LessThanOrEqual", {
|
|
130
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
131
|
-
}>]>;
|
|
132
|
-
}>, Schema.TaggedStruct<"DateRange", {
|
|
133
|
-
readonly lower: Schema.Union<readonly [Schema.TaggedStruct<"GreaterThan", {
|
|
134
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
135
|
-
}>, Schema.TaggedStruct<"GreaterThanOrEqual", {
|
|
136
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
137
|
-
}>]>;
|
|
138
|
-
readonly upper: Schema.optionalKey<Schema.Never>;
|
|
139
|
-
}>, Schema.TaggedStruct<"DateRange", {
|
|
140
|
-
readonly lower: Schema.optionalKey<Schema.Never>;
|
|
141
|
-
readonly upper: Schema.Union<readonly [Schema.TaggedStruct<"LessThan", {
|
|
142
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
143
|
-
}>, Schema.TaggedStruct<"LessThanOrEqual", {
|
|
144
|
-
readonly value: Schema.Codec<InstantExpr, InstantExpr, never, never>;
|
|
145
|
-
}>]>;
|
|
146
|
-
}>]>;
|
|
147
|
-
readonly canonical: Schema.String;
|
|
148
|
-
}>;
|
|
149
|
-
type NaturalCandidate = typeof NaturalCandidate.Type;
|
|
150
150
|
declare const NaturalSuggestion: Schema.Struct<{
|
|
151
151
|
readonly text: Schema.String;
|
|
152
152
|
readonly range: Schema.Union<readonly [Schema.TaggedStruct<"DateRange", {
|
package/dist/language/model.mjs
CHANGED
|
@@ -17,20 +17,17 @@ const NaturalCorrectionCandidate = Schema.Struct({
|
|
|
17
17
|
corrections: Schema.Array(Correction),
|
|
18
18
|
cost: Schema.Int.check(Schema.isGreaterThanOrEqualTo(1))
|
|
19
19
|
});
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
const NaturalCandidate = Schema.Struct({
|
|
21
|
+
range: DateRangeExpr,
|
|
22
|
+
canonical: Schema.String
|
|
23
23
|
});
|
|
24
|
+
const NaturalAlternative = NaturalCandidate;
|
|
24
25
|
const NaturalParseResult = Schema.Struct({
|
|
25
26
|
range: DateRangeExpr,
|
|
26
27
|
quality: ParseQuality,
|
|
27
28
|
corrections: Schema.Array(Correction),
|
|
28
29
|
alternatives: Schema.Array(NaturalAlternative)
|
|
29
30
|
});
|
|
30
|
-
const NaturalCandidate = Schema.Struct({
|
|
31
|
-
range: DateRangeExpr,
|
|
32
|
-
canonical: Schema.String
|
|
33
|
-
});
|
|
34
31
|
const NaturalSuggestion = Schema.Struct({
|
|
35
32
|
text: Schema.String,
|
|
36
33
|
range: DateRangeExpr
|
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
import { LanguageConflictError, LanguageRegistrationError, UnsupportedLocaleError } from "./errors.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { LanguageContributionMetadata, canonicalBaseLocale } from "./model.mjs";
|
|
3
3
|
import { normalizeNaturalText } from "../natural/text.mjs";
|
|
4
4
|
import { Array, Context, Effect, Layer, Match, Option, Order, Ref, Result, Schema } from "effect";
|
|
5
5
|
//#region src/language/registry.ts
|
|
6
6
|
var LanguageRegistry = class extends Context.Service()("chronolizer/LanguageRegistry") {};
|
|
7
|
-
const metadataOf = (contribution) => Match.valueTags(contribution, {
|
|
8
|
-
BaseLanguage: (base) => BaseLanguageMetadata.make({
|
|
9
|
-
locale: base.locale,
|
|
10
|
-
vocabulary: base.vocabulary
|
|
11
|
-
}),
|
|
12
|
-
LanguageExtension: (extension) => LanguageExtensionMetadata.make({
|
|
13
|
-
locale: extension.locale,
|
|
14
|
-
priority: extension.priority,
|
|
15
|
-
vocabulary: extension.vocabulary
|
|
16
|
-
})
|
|
17
|
-
});
|
|
18
7
|
const localeCandidates = (locale) => {
|
|
19
8
|
const candidates = [locale];
|
|
20
9
|
let parent = locale;
|
|
@@ -44,14 +33,7 @@ const compileLanguage = (locale, registered) => {
|
|
|
44
33
|
const vocabulary = Object.freeze(Array.dedupe([...base.contribution.vocabulary, ...Array.flatMap(extensions, (entry) => entry.contribution.vocabulary)]));
|
|
45
34
|
const parsers = Object.freeze([...extensions.map((entry) => entry.contribution.parseExact), base.contribution.parseExact]);
|
|
46
35
|
const suggesters = Object.freeze([...extensions.map((entry) => entry.contribution.suggest), base.contribution.suggest].filter((suggest) => suggest !== void 0));
|
|
47
|
-
const parseExact = (input) =>
|
|
48
|
-
const candidates = [];
|
|
49
|
-
for (const parser of parsers) {
|
|
50
|
-
const candidate = parser(input);
|
|
51
|
-
if (Option.isSome(candidate)) candidates.push(candidate.value);
|
|
52
|
-
}
|
|
53
|
-
return candidates;
|
|
54
|
-
};
|
|
36
|
+
const parseExact = (input) => Array.flatMap(parsers, (parser) => Option.toArray(parser(input)));
|
|
55
37
|
return Option.some(Object.freeze({
|
|
56
38
|
locale: base.contribution.locale,
|
|
57
39
|
vocabulary,
|
|
@@ -65,7 +47,7 @@ const compileLanguage = (locale, registered) => {
|
|
|
65
47
|
const createRegistry = Effect.fn(function* () {
|
|
66
48
|
const entries = yield* Ref.make([]);
|
|
67
49
|
const register = Effect.fn(function* (pluginId, contribution) {
|
|
68
|
-
if (pluginId.length === 0 || !Schema.is(LanguageContributionMetadata)(
|
|
50
|
+
if (pluginId.length === 0 || !Schema.is(LanguageContributionMetadata)(contribution)) return yield* new LanguageRegistrationError({
|
|
69
51
|
pluginId,
|
|
70
52
|
locale: contribution.locale,
|
|
71
53
|
message: "Invalid plugin identifier or contribution metadata"
|
package/dist/locales/cs.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { normalizeNaturalText } from "../natural/text.mjs";
|
|
|
4
4
|
import { defineLanguagePlugin, languagePluginsLayer } from "../language/registry.mjs";
|
|
5
5
|
import { correctWhitespaceSeparatedText } from "../natural/correction.mjs";
|
|
6
6
|
import { completeNaturalPhrases, fixedCalendarPeriodPhrases, naturalCount, prefixNaturalPhrases } from "../natural/suggestion.mjs";
|
|
7
|
-
import { 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";
|
|
7
|
+
import { absoluteDatePeriod, calendarPeriodOffset, candidate, currentYearDatePeriods, datedPeriods, datedQuarterPeriods, fixedDatePeriod, fixedMonthPeriod, fixedQuarterPeriod, fixedYearPeriod, fromNowRange, futurePeriod, futureRange, isoDate, joinedNowCandidate, joinedPeriodCandidate, monthOfRelativeYear, namedCurrentYearDatePeriod, namedDatePeriod, openBoundaryCandidate, parseTrailingCount, periodEndDay, periodRange, periodStartDay, periodToDateRange, periodsFromPhrases, quarterOfRelativeYear, relativePeriod, relativeWeekend, remainingPeriodRange, renderPeriodRange, textAt, trailingPeriod, trailingRange, untilNowRange, validYear } from "./shared.mjs";
|
|
8
8
|
import { Effect, Option, String as String$1 } from "effect";
|
|
9
9
|
//#region src/locales/cs.ts
|
|
10
10
|
const months = [
|
|
@@ -85,6 +85,7 @@ const units = [
|
|
|
85
85
|
many: "dnů",
|
|
86
86
|
pastSingular: "dnem",
|
|
87
87
|
pastPlural: "dny",
|
|
88
|
+
durationGenitive: "dne",
|
|
88
89
|
current: "dnes",
|
|
89
90
|
previous: "včera",
|
|
90
91
|
next: "zítra",
|
|
@@ -98,6 +99,7 @@ const units = [
|
|
|
98
99
|
many: "týdnů",
|
|
99
100
|
pastSingular: "týdnem",
|
|
100
101
|
pastPlural: "týdny",
|
|
102
|
+
durationGenitive: "týdne",
|
|
101
103
|
current: "tento týden",
|
|
102
104
|
previous: "minulý týden",
|
|
103
105
|
next: "příští týden",
|
|
@@ -111,6 +113,7 @@ const units = [
|
|
|
111
113
|
many: "měsíců",
|
|
112
114
|
pastSingular: "měsícem",
|
|
113
115
|
pastPlural: "měsíci",
|
|
116
|
+
durationGenitive: "měsíce",
|
|
114
117
|
current: "tento měsíc",
|
|
115
118
|
previous: "minulý měsíc",
|
|
116
119
|
next: "příští měsíc",
|
|
@@ -124,6 +127,7 @@ const units = [
|
|
|
124
127
|
many: "čtvrtletí",
|
|
125
128
|
pastSingular: "čtvrtletím",
|
|
126
129
|
pastPlural: "čtvrtletími",
|
|
130
|
+
durationGenitive: "čtvrtletí",
|
|
127
131
|
current: "toto čtvrtletí",
|
|
128
132
|
previous: "minulé čtvrtletí",
|
|
129
133
|
next: "příští čtvrtletí",
|
|
@@ -137,6 +141,7 @@ const units = [
|
|
|
137
141
|
many: "let",
|
|
138
142
|
pastSingular: "rokem",
|
|
139
143
|
pastPlural: "lety",
|
|
144
|
+
durationGenitive: "roku",
|
|
140
145
|
current: "tento rok",
|
|
141
146
|
previous: "minulý rok",
|
|
142
147
|
next: "příští rok",
|
|
@@ -144,7 +149,6 @@ const units = [
|
|
|
144
149
|
remaining: "zbytek roku"
|
|
145
150
|
}
|
|
146
151
|
];
|
|
147
|
-
const title = (value) => `${value.slice(0, 1).toLocaleUpperCase("cs")}${value.slice(1)}`;
|
|
148
152
|
const unitAliases = [
|
|
149
153
|
["den", "day"],
|
|
150
154
|
["dny", "day"],
|
|
@@ -360,17 +364,22 @@ const monthNumber = (value) => {
|
|
|
360
364
|
return short === -1 ? void 0 : short + 1;
|
|
361
365
|
};
|
|
362
366
|
const dateLabel = (day, month, year) => `${day}. ${textAt(monthGenitives, month - 1)} ${year}`;
|
|
367
|
+
const currentDateLabel = (day, month) => `${day}. ${textAt(monthGenitives, month - 1)}`;
|
|
363
368
|
const parseNamedDate = (input) => {
|
|
364
369
|
const named = String$1.match(/^([0-3]?\d)\.?(?: )([a-záčďéěíňóřšťúůýž]+\.?) (\d{4})$/u)(input);
|
|
365
370
|
if (Option.isSome(named)) return namedDatePeriod(textAt(named.value, 3), textAt(named.value, 2), textAt(named.value, 1), monthNumber, dateLabel);
|
|
366
371
|
const numeric = String$1.match(/^([0-3]?\d)(?:\. ?|[/-])([01]?\d)(?:\. ?|[/-])(\d{4})$/u)(input);
|
|
367
|
-
if (Option.
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
372
|
+
if (Option.isSome(numeric)) {
|
|
373
|
+
const year = validYear(textAt(numeric.value, 3));
|
|
374
|
+
const month = Number(textAt(numeric.value, 2));
|
|
375
|
+
const day = Number(textAt(numeric.value, 1));
|
|
376
|
+
if (year !== void 0 && month >= 1 && month <= 12) {
|
|
377
|
+
const value = isoDate(year, month, day);
|
|
378
|
+
if (isIsoDate(value) && value !== "9999-12-31") return Option.some(fixedDatePeriod(value, dateLabel(day, month, year)));
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
const current = String$1.match(/^([0-3]?\d)\.? ([a-záčďéěíňóřšťúůýž]+\.?)$/u)(input);
|
|
382
|
+
return Option.isSome(current) ? namedCurrentYearDatePeriod(textAt(current.value, 2), textAt(current.value, 1), monthNumber, currentDateLabel) : Option.none();
|
|
374
383
|
};
|
|
375
384
|
const quarterNumber = (value) => {
|
|
376
385
|
if (value.startsWith("q") && value.length === 2) return Number(value.slice(1));
|
|
@@ -398,7 +407,8 @@ const parseQuarter = (input) => {
|
|
|
398
407
|
return quarter === void 0 ? Option.none() : Option.some(quarterOfRelativeYear(quarter, 0, `Q${quarter}`));
|
|
399
408
|
};
|
|
400
409
|
const parseBasePeriod = (input) => {
|
|
401
|
-
|
|
410
|
+
const absoluteDate = absoluteDatePeriod(input, "cs");
|
|
411
|
+
if (Option.isSome(absoluteDate)) return absoluteDate;
|
|
402
412
|
const namedDate = parseNamedDate(input);
|
|
403
413
|
if (Option.isSome(namedDate)) return namedDate;
|
|
404
414
|
const quarter = parseQuarter(input);
|
|
@@ -412,7 +422,7 @@ const parseBasePeriod = (input) => {
|
|
|
412
422
|
if (Option.isSome(monthYear)) {
|
|
413
423
|
const month = monthNumber(textAt(monthYear.value, 1));
|
|
414
424
|
const year = validYear(textAt(monthYear.value, 2));
|
|
415
|
-
if (month !== void 0 && year !== void 0) return Option.some(fixedMonthPeriod(year, month, `${
|
|
425
|
+
if (month !== void 0 && year !== void 0) return Option.some(fixedMonthPeriod(year, month, `${textAt(months, month - 1)} ${year}`));
|
|
416
426
|
}
|
|
417
427
|
const relativeMonth = String$1.match(/^([a-záčďéěíňóřšťúůýž]+\.?) (minulého roku|příštího roku|tohoto roku)$/u)(input);
|
|
418
428
|
const relativeMonthYearFirst = String$1.match(/^(minulého roku|příštího roku|tohoto roku) ([a-záčďéěíňóřšťúůýž]+\.?)$/u)(input);
|
|
@@ -423,10 +433,10 @@ const parseBasePeriod = (input) => {
|
|
|
423
433
|
const yearText = textAt(relativeMatch.value, yearFirst ? 1 : 2);
|
|
424
434
|
const month = monthNumber(monthText);
|
|
425
435
|
const direction = relativeYearDirection(yearText);
|
|
426
|
-
if (month !== void 0) return Option.some(monthOfRelativeYear(month, direction, `${
|
|
436
|
+
if (month !== void 0) return Option.some(monthOfRelativeYear(month, direction, `${textAt(months, month - 1)} ${relativeYearName(direction)}`));
|
|
427
437
|
}
|
|
428
438
|
const standaloneMonth = monthNumber(input);
|
|
429
|
-
if (standaloneMonth !== void 0) return Option.some(monthOfRelativeYear(standaloneMonth, 0,
|
|
439
|
+
if (standaloneMonth !== void 0) return Option.some(monthOfRelativeYear(standaloneMonth, 0, textAt(months, standaloneMonth - 1)));
|
|
430
440
|
const alias = periodAliases.find((entry) => entry[0] === input);
|
|
431
441
|
if (alias !== void 0) return Option.some(relativePeriod(alias[1], alias[2], alias[3]));
|
|
432
442
|
if (["víkend", "tento víkend"].includes(input)) return Option.some(relativeWeekend(0, "tento víkend"));
|
|
@@ -464,6 +474,24 @@ const rollingFuturePatterns = [countedPattern("^(?:příští|následující) ([
|
|
|
464
474
|
const rollingSincePattern = countedPattern("^za poslední ([1-9]\\d*) (UNIT)$");
|
|
465
475
|
const rollingBarePattern = countedPattern("^([1-9]\\d*) (UNIT)$");
|
|
466
476
|
const firstPatternMatch = (input, patterns) => Option.firstSomeOf(patterns.map((pattern) => String$1.match(pattern)(input)));
|
|
477
|
+
const singularRollingPhrases = units.flatMap((entry) => [
|
|
478
|
+
{
|
|
479
|
+
phrase: `poslední ${entry.singular}`,
|
|
480
|
+
entry,
|
|
481
|
+
future: false
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
phrase: `během posledního ${entry.durationGenitive}`,
|
|
485
|
+
entry,
|
|
486
|
+
future: false
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
phrase: `během následujícího ${entry.durationGenitive}`,
|
|
490
|
+
entry,
|
|
491
|
+
future: true
|
|
492
|
+
}
|
|
493
|
+
]);
|
|
494
|
+
const singularRollingCanonical = (entry, future) => `během ${future ? "následujícího" : "posledního"} ${entry.durationGenitive}`;
|
|
467
495
|
const countNoun = (amount, entry) => {
|
|
468
496
|
if (amount === 1) return entry.singular;
|
|
469
497
|
const lastTwo = amount % 100;
|
|
@@ -487,6 +515,11 @@ const parseCalendarOffset = (input) => {
|
|
|
487
515
|
return Option.some(candidate(periodRange(relativePeriod(unit, direction, canonical)), canonical));
|
|
488
516
|
};
|
|
489
517
|
const parseRollingPeriod = (input) => {
|
|
518
|
+
const singular = singularRollingPhrases.find((entry) => entry.phrase === input);
|
|
519
|
+
if (singular !== void 0) {
|
|
520
|
+
const range = singular.future ? futureRange(1, singular.entry.unit) : trailingRange(1, singular.entry.unit);
|
|
521
|
+
return Option.some(candidate(range, singularRollingCanonical(singular.entry, singular.future)));
|
|
522
|
+
}
|
|
490
523
|
const since = String$1.match(rollingSincePattern)(input);
|
|
491
524
|
const past = firstPatternMatch(input, rollingPastPatterns);
|
|
492
525
|
const bare = String$1.match(rollingBarePattern)(input);
|
|
@@ -505,9 +538,22 @@ const parseRollingPeriod = (input) => {
|
|
|
505
538
|
if (entry === void 0) return Option.none();
|
|
506
539
|
const isFuture = Option.isSome(future);
|
|
507
540
|
const range = isFuture ? futureRange(amount.value, unit) : trailingRange(amount.value, unit);
|
|
541
|
+
if (amount.value === 1) return Option.some(candidate(range, singularRollingCanonical(entry, isFuture)));
|
|
508
542
|
const modifier = isFuture ? "příští" : "poslední";
|
|
509
543
|
return Option.some(candidate(range, `${modifier} ${amount.value} ${countNoun(amount.value, entry)}`));
|
|
510
544
|
};
|
|
545
|
+
const parseElidedDateRange = (input) => {
|
|
546
|
+
const joined = String$1.match(/^od ([0-3]?\d)\.?(?: do) ([0-3]?\d)\.? ([a-záčďéěíňóřšťúůýž]+\.?)(?: (\d{4}))?$/u)(input);
|
|
547
|
+
const dashed = String$1.match(/^([0-3]?\d)\.?[–—-]([0-3]?\d)\.? ([a-záčďéěíňóřšťúůýž]+\.?)(?: (\d{4}))?$/u)(input);
|
|
548
|
+
const match = Option.firstSomeOf([joined, dashed]);
|
|
549
|
+
if (Option.isNone(match)) return Option.none();
|
|
550
|
+
const lowerDay = textAt(match.value, 1);
|
|
551
|
+
const upperDay = textAt(match.value, 2);
|
|
552
|
+
const month = textAt(match.value, 3);
|
|
553
|
+
const year = textAt(match.value, 4);
|
|
554
|
+
const suffix = year.length === 0 ? "" : ` ${year}`;
|
|
555
|
+
return joinedPeriodCandidate(`od ${lowerDay}. ${month}${suffix} do ${upperDay}. ${month}${suffix}`, [["od ", " do "]], parsePeriod, (lower, upper) => `od ${lower} do ${upper} včetně`);
|
|
556
|
+
};
|
|
511
557
|
const boundaryCandidate = (input) => {
|
|
512
558
|
const included = String$1.match(/^do (.+) včetně$/u)(input);
|
|
513
559
|
if (Option.isSome(included)) return openBoundaryCandidate(`do ${textAt(included.value, 1)}`, [["do ", "through"]], parsePeriod);
|
|
@@ -539,6 +585,8 @@ const parseCzech = (input) => {
|
|
|
539
585
|
if (Option.isSome(rolling)) return rolling;
|
|
540
586
|
const toDate = toDatePhrases.find((entry) => entry.phrase === input);
|
|
541
587
|
if (toDate !== void 0) return Option.some(candidate(periodToDateRange(toDate.entry.unit), toDate.entry.toDate));
|
|
588
|
+
const elided = parseElidedDateRange(input);
|
|
589
|
+
if (Option.isSome(elided)) return elided;
|
|
542
590
|
const nowBounded = joinedNowCandidate(input, [["od ", " do dneška"], ["mezi ", " a dneškem"]], ["od dneška do ", "mezi dneškem a "], parsePeriod, (period) => `od ${period} do dneška`, (period) => `od dneška do ${period}`);
|
|
543
591
|
if (Option.isSome(nowBounded)) return nowBounded;
|
|
544
592
|
const bounded = joinedPeriodCandidate(input, [
|
|
@@ -551,7 +599,7 @@ const parseCzech = (input) => {
|
|
|
551
599
|
if (Option.isSome(bounded)) return bounded;
|
|
552
600
|
const boundary = boundaryCandidate(input);
|
|
553
601
|
if (Option.isSome(boundary)) return boundary;
|
|
554
|
-
return Option.map(
|
|
602
|
+
return parsePeriod(input).pipe(Option.map((period) => candidate(periodRange(period), period.canonical)));
|
|
555
603
|
};
|
|
556
604
|
const staticPeriodPhrases = [
|
|
557
605
|
...periodAliases.map((entry) => entry[0]),
|
|
@@ -606,6 +654,7 @@ const countedSuggestions = (input) => {
|
|
|
606
654
|
const czechSuggestionPhrases = [
|
|
607
655
|
...units.map((entry) => entry.toDate),
|
|
608
656
|
...units.map((entry) => entry.remaining),
|
|
657
|
+
...singularRollingPhrases.map((entry) => entry.phrase),
|
|
609
658
|
...staticPeriodPhrases,
|
|
610
659
|
...prefixNaturalPhrases(staticPeriodPhrases, boundaryPrefixes),
|
|
611
660
|
"dosud",
|
|
@@ -634,14 +683,18 @@ const renderCzech = (range) => {
|
|
|
634
683
|
const future = futurePeriod(range);
|
|
635
684
|
if (Option.isSome(future)) {
|
|
636
685
|
const entry = units.find((unit) => unit.unit === future.value.unit);
|
|
637
|
-
if (entry !== void 0) return Option.some(`příští ${future.value.amount} ${countNoun(future.value.amount, entry)}`);
|
|
686
|
+
if (entry !== void 0) return Option.some(future.value.amount === 1 ? singularRollingCanonical(entry, true) : `příští ${future.value.amount} ${countNoun(future.value.amount, entry)}`);
|
|
638
687
|
}
|
|
639
688
|
const trailing = trailingPeriod(range);
|
|
640
689
|
if (Option.isSome(trailing)) {
|
|
641
690
|
const entry = units.find((unit) => unit.unit === trailing.value.unit);
|
|
642
|
-
if (entry !== void 0) return Option.some(`poslední ${trailing.value.amount} ${countNoun(trailing.value.amount, entry)}`);
|
|
691
|
+
if (entry !== void 0) return Option.some(trailing.value.amount === 1 ? singularRollingCanonical(entry, false) : `poslední ${trailing.value.amount} ${countNoun(trailing.value.amount, entry)}`);
|
|
643
692
|
}
|
|
644
|
-
const periods = [
|
|
693
|
+
const periods = [
|
|
694
|
+
...staticPeriods,
|
|
695
|
+
...currentYearDatePeriods(range, currentDateLabel),
|
|
696
|
+
...periodsFromPhrases([...datedPeriods(range, months), ...datedQuarterPeriods(range)], parsePeriod)
|
|
697
|
+
];
|
|
645
698
|
return renderPeriodRange(range, [...units.map((entry) => candidate(periodToDateRange(entry.unit), entry.toDate)), ...units.map((entry) => candidate(remainingPeriodRange(entry.unit), entry.remaining))], periods, (period) => `od ${period}`, (period) => `před ${period}`, (period) => `do ${period} včetně`, (period) => `po ${period}`, (lower, upper) => `od ${lower} do ${upper} včetně`, (period) => `od ${period} do dneška`, (period) => `od dneška do ${period}`, () => "dosud", () => "od nynějška");
|
|
646
699
|
};
|
|
647
700
|
const CzechContribution = new BaseLanguageContribution({
|