cronli5 0.3.4 → 0.8.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/CHANGELOG.md +120 -0
- package/README.md +63 -13
- package/cronli5.min.js +2 -2
- package/dist/cronli5.cjs +72 -9
- package/dist/cronli5.js +72 -9
- package/dist/lang/de.cjs +14 -6
- package/dist/lang/de.js +14 -6
- package/dist/lang/en.cjs +14 -6
- package/dist/lang/en.js +14 -6
- package/dist/lang/es.cjs +14 -6
- package/dist/lang/es.js +14 -6
- package/dist/lang/fi.cjs +14 -6
- package/dist/lang/fi.js +14 -6
- package/dist/lang/fr.cjs +1211 -0
- package/dist/lang/fr.js +1187 -0
- package/dist/lang/pt.cjs +1592 -0
- package/dist/lang/pt.js +1568 -0
- package/dist/lang/zh.cjs +58 -9
- package/dist/lang/zh.js +58 -9
- package/package.json +13 -2
- package/src/core/cadence.ts +25 -12
- package/src/core/index.ts +7 -3
- package/src/core/quartz.ts +97 -0
- package/src/core/schedule.ts +1 -0
- package/src/core/specs.ts +2 -2
- package/src/cronli5.ts +20 -3
- package/src/lang/de/index.ts +3 -2
- package/src/lang/en/index.ts +3 -2
- package/src/lang/es/index.ts +3 -2
- package/src/lang/fi/index.ts +3 -2
- package/src/lang/fr/dialects.ts +49 -0
- package/src/lang/fr/index.ts +2116 -0
- package/src/lang/fr/notes.md +280 -0
- package/src/lang/fr/status.json +8 -0
- package/src/lang/pt/dialects.ts +56 -0
- package/src/lang/pt/index.ts +2804 -0
- package/src/lang/pt/notes.md +199 -0
- package/src/lang/pt/status.json +8 -0
- package/src/lang/zh/index.ts +61 -5
- package/src/lang/zh/notes.md +16 -4
- package/src/lang/zh/status.json +10 -1
- package/src/types.ts +44 -0
- package/types/core/cadence.d.ts +1 -0
- package/types/core/quartz.d.ts +4 -0
- package/types/core/schedule.d.ts +1 -0
- package/types/cronli5.d.ts +4 -4
- package/types/lang/fr/dialects.d.ts +11 -0
- package/types/lang/fr/index.d.ts +4 -0
- package/types/lang/pt/dialects.d.ts +13 -0
- package/types/lang/pt/index.d.ts +4 -0
- package/types/types.d.ts +39 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# Français (fr, target fr-FR) — Language Notes
|
|
2
|
+
|
|
3
|
+
**donor: es.** Derived by sibling-derivation (tooling/docs/language-pipeline.md):
|
|
4
|
+
the Spanish module supplies the structure, plan override, OR-frame, predicates,
|
|
5
|
+
re-strategies, and dialect mechanism; this doc records only where **fr-FR
|
|
6
|
+
diverges** from that donor. The es→fr gap is wider than es→pt (a deliberate
|
|
7
|
+
stress test of the workflow), so several divergences below need genuine renderer
|
|
8
|
+
logic, not string swaps. The shipped table today is **fr-FR**; fr-CA is a future
|
|
9
|
+
dialect axis (below). **pt** is referenced where it solved an analogous Romance
|
|
10
|
+
problem (contractions, gender) — but fr is authored fresh and **never imports
|
|
11
|
+
pt**: the only cross-module reference is the donor (es). The donor's contract is
|
|
12
|
+
[`../es/notes.md`](../es/notes.md); pt's analogous layer is
|
|
13
|
+
[`../pt/notes.md`](../pt/notes.md).
|
|
14
|
+
|
|
15
|
+
## Anchors
|
|
16
|
+
|
|
17
|
+
French norm (Imprimerie nationale / Académie française, plus cronstrue `fr`):
|
|
18
|
+
lowercase month and weekday names, **24-hour clock by default** with the **`h`
|
|
19
|
+
separator** between hour and minute. **Decided: spaced "9 h 30" form**
|
|
20
|
+
(Imprimerie nationale: a thin/regular space each side of `h`), hour rendered
|
|
21
|
+
**unpadded** ("1 h", "9 h", "17 h 30"), top-of-hour as bare "9 h" (no "h 00").
|
|
22
|
+
Rationale: the spaced `h` is the typographic standard fr-FR reference (IN
|
|
23
|
+
*Lexique des règles typographiques*); the colon "09:30" reads as Anglophone/SI
|
|
24
|
+
and the unspaced "9h30" is the casual register. **Ratified** by the blind fr-FR
|
|
25
|
+
native panel (everyday / copy-editor / technical, 2026-06-27): spaced "9 h 30" is
|
|
26
|
+
the default, unspaced "9h30" remains the opt-in dialect register.
|
|
27
|
+
|
|
28
|
+
**minuit / midi for exact 0:00 / 12:00** (replacing es "medianoche/mediodía").
|
|
29
|
+
These are **bare nouns, no article and no `h`** — "à minuit", "à midi" (not "à
|
|
30
|
+
0 h" / "à 12 h"). minuit is masculine, midi is masculine.
|
|
31
|
+
|
|
32
|
+
**minuit/midi are the exact-POINT form only — panel-ratified (2026-06-27).** A
|
|
33
|
+
range *over* the midnight or noon hour (the whole 0:00–0:59 / 12:00–12:59 hour
|
|
34
|
+
as a per-hour window) must **not** mix the word with a numeric endpoint: render
|
|
35
|
+
**"de 0 h à 0 h 59"**, **"de 12 h à 12 h 59"** — never "de minuit à 0 h 59" /
|
|
36
|
+
"de midi à 12 h 59" (the panel found the word+numeric mix within one window
|
|
37
|
+
jarring and register-inconsistent). minuit/midi are reserved for the bare exact
|
|
38
|
+
instant ("à minuit") and for a cadence/range whose endpoint genuinely **is** the
|
|
39
|
+
0:00 / 12:00 point ("toutes les sept heures **de minuit** à 21 h", "de minuit à
|
|
40
|
+
5 h 59" — minuit is the real first fire there, not a 0:00–0:59 window).
|
|
41
|
+
|
|
42
|
+
**Seconds-clock: "H h MM min SS s", with the zero-minute SUPPRESSED —
|
|
43
|
+
panel-ratified (2026-06-27).** The full hours/minutes/seconds form is "9 h 30 min
|
|
44
|
+
15 s" (SI abbreviations `min` / `s`, spaced per the `h` convention). When the
|
|
45
|
+
minute is **zero**, drop the "0 min" segment entirely: **"9 h 30 s"** (not
|
|
46
|
+
"9 h 0 min 30 s") — nobody says "neuf heures zéro minute trente secondes". Keep
|
|
47
|
+
"min" only when the minute is non-zero.
|
|
48
|
+
|
|
49
|
+
**Bare-numeral hour lists carry the `h` — panel-ratified (2026-06-27).** An
|
|
50
|
+
index-style active-hours list ("pendant les heures de …") uses the **"X h"** form
|
|
51
|
+
for every value, consistent with the clock convention everywhere else: "pendant
|
|
52
|
+
les heures de 0 h, 3 h, 6 h, …" (not the bare "0, 3, 6, …" the donor's
|
|
53
|
+
unit-less list would give).
|
|
54
|
+
|
|
55
|
+
**12-hour {ampm} mode — decided: NOT supported; {ampm} is a documented no-op
|
|
56
|
+
for fr.** Rationale: fr-FR overwhelmingly uses the 24-hour clock in writing and
|
|
57
|
+
speech; "du matin / de l'après-midi / du soir" exist but are colloquial qualifiers
|
|
58
|
+
on an already-spoken hour, not a true 12-hour clock with an AM/PM mark, and fr has
|
|
59
|
+
no clean 12-noon-boundary meridiem. So fr ships **24h-only**; an explicit
|
|
60
|
+
`{ampm: true}` is accepted and ignored (no throw). This is a real es→fr
|
|
61
|
+
divergence: es made {ampm} a first-class clock; fr declines it. The es day-period
|
|
62
|
+
band machinery (madrugada/mañana/tarde/noche) therefore has **no fr analog** and
|
|
63
|
+
is dropped, not ported. **Ratified** by the fr-FR panel (2026-06-27): the 24h-only
|
|
64
|
+
decline is the safe, idiomatic default.
|
|
65
|
+
|
|
66
|
+
## Per-value ordinals (the named fr hazard)
|
|
67
|
+
|
|
68
|
+
es used invariant date forms ("el 1", "el día N"). fr requires a **per-VALUE**
|
|
69
|
+
rule the renderer must implement:
|
|
70
|
+
|
|
71
|
+
- **The 1st of the month is "le 1er"** (premier); **every other day is the bare
|
|
72
|
+
cardinal with the article — "le 2", "le 15", "le 31".** This is a deep fr-FR
|
|
73
|
+
norm (calendars, official/legal texts, speech): "le premier janvier" but "le
|
|
74
|
+
2 janvier". The renderer selects per value, not once per field.
|
|
75
|
+
- The "1er" carries into **ranges** (first term only — "du 1er au 15"), **lists**
|
|
76
|
+
("le 1er, le 15 et le 20"), and **OR-union date arms** ("le 1er de chaque
|
|
77
|
+
mois"). Every other position stays cardinal.
|
|
78
|
+
- **Ratified** by the fr-FR panel (2026-06-27) as correct and natural; it remains
|
|
79
|
+
one of the three es→fr stress points needing real per-value logic (with
|
|
80
|
+
contractions and gender).
|
|
81
|
+
|
|
82
|
+
## Quartz nth-weekday ordinals (gendered)
|
|
83
|
+
|
|
84
|
+
es used invariant "primer/último". fr nth ordinals **agree in gender** with the
|
|
85
|
+
target noun: **premier/première, deuxième, troisième, quatrième, cinquième,
|
|
86
|
+
dernier/dernière.** Weekdays are **masculine** in fr (le lundi), so
|
|
87
|
+
"le premier lundi du mois", "le dernier vendredi du mois". "le dernier jour du
|
|
88
|
+
mois" (jour masculine). The feminine "première/dernière" form is needed for any
|
|
89
|
+
feminine target noun (e.g. "la dernière semaine" if a week-scoped form arises).
|
|
90
|
+
gender selection is renderer logic, like pt; but fr weekdays are masculine
|
|
91
|
+
(unlike pt's feminine -feira), so the common case is the masculine ordinal. The
|
|
92
|
+
gendered-ordinal selection was **ratified** by the fr-FR panel (2026-06-27).
|
|
93
|
+
|
|
94
|
+
## W operator: "ouvrable", not "ouvré"
|
|
95
|
+
|
|
96
|
+
The Quartz `W` / `LW` tokens (nearest weekday to a date / last weekday of the
|
|
97
|
+
month) render with **"ouvrable"** — "le jour ouvrable le plus proche du 15", "le
|
|
98
|
+
dernier jour ouvrable du mois". **Panel-ratified (2026-06-27), replacing the
|
|
99
|
+
candidate's "ouvré".** Rationale: *ouvrable* = a legally-workable day (the
|
|
100
|
+
calendar-defined Mon–Fri/non-holiday day the `W` token actually selects), whereas
|
|
101
|
+
*ouvré* = an actually-worked day (a payroll/accounting term implying the day was
|
|
102
|
+
in fact worked). `W` is a calendar predicate, not a worked-time count, so
|
|
103
|
+
*ouvrable* is the precise term.
|
|
104
|
+
|
|
105
|
+
## Contractions (es lacks these; pt solved the analogous problem)
|
|
106
|
+
|
|
107
|
+
French fuses *de* and *à* with the masculine/plural definite article; the
|
|
108
|
+
renderer must form these wherever es emitted a bare preposition + article
|
|
109
|
+
(pt's contraction layer is the reference approach — gender/number-driven
|
|
110
|
+
formation, not string substitution):
|
|
111
|
+
|
|
112
|
+
- *de* + le → **du**; *de* + les → **des**. (*de* + la → **de la**, *de* +
|
|
113
|
+
l' → **de l'** stay unfused.)
|
|
114
|
+
- *à* + le → **au**; *à* + les → **aux**. (*à* + la → **à la**, *à* + l' →
|
|
115
|
+
**à l'** stay unfused.)
|
|
116
|
+
|
|
117
|
+
Where the renderer needs them:
|
|
118
|
+
- **"de chaque mois / de chaque heure"** — de + chaque (no article, no fusion);
|
|
119
|
+
but **"du mois"** (de+le mois) in date-range scopes ("du 1er au 15 **du
|
|
120
|
+
mois**").
|
|
121
|
+
- **clock list / "at"** — fr uses bare **"à"** + the time ("à 9 h 30"), and
|
|
122
|
+
**"à minuit" / "à midi"**; no article on a clock time, so no fusion there
|
|
123
|
+
(unlike es "a las"). The fusion bites on **date/scope** nouns, not the clock.
|
|
124
|
+
- **date ranges** — "du 1er au 15", "de juin à septembre" (de+proper-noun, no
|
|
125
|
+
fusion), "du lundi au vendredi" (de+le, à+le → du/au).
|
|
126
|
+
- **windows / cadences** — per-hour minute windows read "de 9 h à 9 h 59"
|
|
127
|
+
(de/à + bare hour, no article, no fusion); an hour cadence "toutes les deux
|
|
128
|
+
heures **de** 9 h **à** 17 h".
|
|
129
|
+
|
|
130
|
+
**FLAGGED** as the principal structural es→fr divergence and the expected RED in
|
|
131
|
+
the TDD port — gender/number-driven formation, mirroring pt's layer.
|
|
132
|
+
|
|
133
|
+
## Gender and agreement
|
|
134
|
+
|
|
135
|
+
es is largely invariant; fr needs agreement the renderer must handle:
|
|
136
|
+
|
|
137
|
+
- **Weekdays are masculine** (le lundi … le dimanche); **months masculine**
|
|
138
|
+
(janvier … décembre). This drives "le lundi", "le premier lundi".
|
|
139
|
+
- **"chaque" is invariant** (chaque heure, chaque mois, chaque jour) — no gender
|
|
140
|
+
choice, simpler than es "cada"/"todos". **"tous les jours"** (m.pl.) for "every
|
|
141
|
+
day"; **"toutes les heures"** (f.pl.), **"tous les mois"** (m.pl.) for the
|
|
142
|
+
cadence determiner. *heure* is feminine, *mois/jour* masculine — the
|
|
143
|
+
determiner agrees.
|
|
144
|
+
- **Cardinal "deux" etc. are invariant** in "toutes les deux heures" (no
|
|
145
|
+
feminine inflection of the number itself), simpler than pt's "duas horas".
|
|
146
|
+
- Quartz nth ordinals agree with the (masculine) weekday — see above.
|
|
147
|
+
|
|
148
|
+
## Weekday recurrence
|
|
149
|
+
|
|
150
|
+
es uses the plural article "los lunes" = every Monday. **Decided: "le lundi"**
|
|
151
|
+
(singular definite article = the habitual/recurrent every-Monday), the standard
|
|
152
|
+
fr-FR generic-recurrence form — "le lundi" already means "on Mondays / every
|
|
153
|
+
Monday", so a plural "les lundis" is **not** used (it reads as several specific
|
|
154
|
+
Mondays, the wrong sense). **Ratified** by the fr-FR panel (2026-06-27): "le
|
|
155
|
+
lundi" (singular definite habitual) is the default.
|
|
156
|
+
|
|
157
|
+
- **Multi-day lists stay singular-definite — panel-ratified (2026-06-27), a
|
|
158
|
+
deliberate es divergence.** A weekday list repeats the singular "le" per day:
|
|
159
|
+
**"le mardi, le jeudi, le samedi et le dimanche"** — *not* the es-style plural
|
|
160
|
+
"les mardis, les jeudis, …". es pluralizes ("los martes, jueves, …"); fr keeps
|
|
161
|
+
the distributive singular habitual, consistent with the single-day "le lundi".
|
|
162
|
+
Applies uniformly to every weekday-list entry (plain lists and OR-union DOW arms
|
|
163
|
+
alike).
|
|
164
|
+
- **Ranges: "du lundi au vendredi"** (de+le → du, à+le → au), the idiomatic fr
|
|
165
|
+
weekday range.
|
|
166
|
+
- **Lists: "le lundi, le mercredi et le vendredi"** — the article repeats per
|
|
167
|
+
day (no suffix-ellipsis like pt's *-feira*; fr has no such affix). A bare-noun
|
|
168
|
+
list "lundi, mercredi et vendredi" reads as *this coming* set, so the article
|
|
169
|
+
is kept for the recurrence sense.
|
|
170
|
+
- **No "tous les" on weekdays** — "le lundi" already carries "every", so
|
|
171
|
+
"tous les lundis" would be redundant emphasis (parallels es's "no todos on
|
|
172
|
+
weekdays" rule; "tous les jours" stays because "les jours" alone is not "every
|
|
173
|
+
day").
|
|
174
|
+
|
|
175
|
+
## Connectives
|
|
176
|
+
|
|
177
|
+
- and → **et** (no comma before *et* in a simple series — fr, like pt, has **no**
|
|
178
|
+
RAE-style "coma ante y"; the es day-period-join comma re-strategy is **dropped**,
|
|
179
|
+
not ported. Moot anyway since fr has no day periods.)
|
|
180
|
+
- or → **ou** (the OR-union connector; see OR-union frame).
|
|
181
|
+
- range / until → **à** ("de … à …", "du … au …") and **jusqu'à** where a
|
|
182
|
+
terminal "until" reads better; default to **à** to mirror es "de … a …".
|
|
183
|
+
|
|
184
|
+
## OR-union frame (date-OR-weekday)
|
|
185
|
+
|
|
186
|
+
es "ya sea X o Y" → **decided: "soit X soit Y"** (the fr either-or correlative),
|
|
187
|
+
e.g. "le 1er de chaque mois, soit le 1er, soit le lundi". Rationale: "soit …
|
|
188
|
+
soit …" is the unambiguous fr inclusive-alternative correlative and reads as the
|
|
189
|
+
union of two independent day conditions, parallel to es "ya sea … o". A bare
|
|
190
|
+
"X ou Y" risks an intersection misread when the arms are themselves complex; the
|
|
191
|
+
"soit … soit" frame brackets each arm. **Ratified** by the fr-FR panel
|
|
192
|
+
(2026-06-27): "soit … soit …" is the precision-appropriate **inclusive**-union
|
|
193
|
+
correlative (the technical reviewer confirmed the inclusive reading is standard in
|
|
194
|
+
technical prose; all three personas read the union, not an intersection or an
|
|
195
|
+
exclusive or). The shared month is fronted once and the arms are month-less,
|
|
196
|
+
exactly as in es. The weekday arm reads the fr recurrence ("le lundi" / "du lundi
|
|
197
|
+
au vendredi"); a **single-weekday** arm reads **"n'importe quel lundi"** (es
|
|
198
|
+
"cualquier lunes"), and a **range** arm keeps a nominal head: **"n'importe quel
|
|
199
|
+
jour du lundi au vendredi"**. Both "n'importe quel …" forms ratified by the panel
|
|
200
|
+
as idiomatic and unambiguous.
|
|
201
|
+
|
|
202
|
+
## Names
|
|
203
|
+
|
|
204
|
+
- **Lowercase months and weekdays** (confirmed fr-FR norm — Académie/IN: janvier,
|
|
205
|
+
lundi are common nouns, never capitalized mid-sentence).
|
|
206
|
+
|
|
207
|
+
## Ported re-strategies (language-neutral; fr forms)
|
|
208
|
+
|
|
209
|
+
- **Per-hour windows for wildcard minutes over hour lists** (es §wildcard minutes
|
|
210
|
+
→ per-hour windows): keep the strategy; fr form **"de 9 h à 9 h 59"** (bare
|
|
211
|
+
hours, de/à, no article). A shared scope is said once per range; the es 12-hour
|
|
212
|
+
day-period folding has no fr analog (24h-only), so each window is a plain hour
|
|
213
|
+
span.
|
|
214
|
+
- **OR-union unified frame** — the "soit … soit …" frame above; month fronted
|
|
215
|
+
once, arms month-less, exactly as es.
|
|
216
|
+
- **No-fold month range** — a month range never folds into another phrase
|
|
217
|
+
("le 1er juin à septembre" parses as "(le 1er juin) à septembre"); dates scope
|
|
218
|
+
it instead ("le 1er de chaque mois, de juin à septembre"); mixed lists repeat
|
|
219
|
+
the preposition per piece ("en janvier et de mars à juin"). Same rule as es and
|
|
220
|
+
English.
|
|
221
|
+
- **Step-flattening** — step segments inside lists always flatten into their
|
|
222
|
+
fires (months, weekdays, dates, minutes, seconds); no raw step token reaches
|
|
223
|
+
the output. Identical to es.
|
|
224
|
+
- **Anchored minutes/seconds** read as **"à la minute 30 de chaque heure"**
|
|
225
|
+
(à+la → no fusion; de+chaque → no fusion), the donor's "en el minuto 30 de
|
|
226
|
+
cada hora" — not a calque of "past the hour".
|
|
227
|
+
|
|
228
|
+
## Dialect axis (future)
|
|
229
|
+
|
|
230
|
+
fr-CA (OQLF / Canadian French) is a **future dialect** — it carries clock and
|
|
231
|
+
lexical/typographic divergences from fr-FR (e.g. OQLF prefers the non-spaced or
|
|
232
|
+
differently-spaced `h`, and some date register differs), mirroring es's
|
|
233
|
+
es-ES / es-419 split and pt's pt-PT axis. **One `fr` table today = fr-FR.** A
|
|
234
|
+
future `fr-CA` (and any colloquial-clock custom field such as an unspaced "9h30"
|
|
235
|
+
style) would clear its own native panel before shipping, per the dialect rules
|
|
236
|
+
in the pipeline.
|
|
237
|
+
|
|
238
|
+
## Anticipated renderer divergences (the es→fr stress points)
|
|
239
|
+
|
|
240
|
+
Recorded for Stage-4 port — where the RED is expected, the analogue of pt's
|
|
241
|
+
contraction/gender layer but wider:
|
|
242
|
+
|
|
243
|
+
1. **Contractions (du/des/au/aux)** — gender/number-driven fusion of de/à + the
|
|
244
|
+
article, on date/scope nouns (not the clock). Net-new vs es.
|
|
245
|
+
2. **Per-value ordinals (le 1er vs le N)** — a per-value selector on dates, not
|
|
246
|
+
the invariant es form. Net-new logic.
|
|
247
|
+
3. **Gender/ordinal agreement** — premier/première, dernier/dernière selected by
|
|
248
|
+
target-noun gender; masculine weekdays/months; the agreeing cadence
|
|
249
|
+
determiner (toutes les heures / tous les mois).
|
|
250
|
+
4. **Clock formatter** — "9 h 30" / "1 h" (unpadded, spaced `h`) and the bare
|
|
251
|
+
minuit/midi, replacing es's padded "09:30" + article + day-period machinery;
|
|
252
|
+
{ampm} declined (no-op).
|
|
253
|
+
5. **Recurrence head** — "le lundi" singular-definite, replacing es's plural
|
|
254
|
+
"los lunes".
|
|
255
|
+
|
|
256
|
+
## Residual inherited from es (panel-flagged, NOT fixed here)
|
|
257
|
+
|
|
258
|
+
The blind fr-FR panel (2026-06-27) found one structural residual it did **not**
|
|
259
|
+
ask fr to fix in isolation, because it is inherited from the es donor and shows
|
|
260
|
+
identically in es (and pt):
|
|
261
|
+
|
|
262
|
+
- **Double-"et" boundary on `* 2/4,18-20 * * *`.** The hour field unions a step
|
|
263
|
+
segment {2,6,10,14,18,22} with a range {18,19,20}; the renderer emits the
|
|
264
|
+
step segment's per-hour windows and then the range window, joining them with a
|
|
265
|
+
second "et": "… et de 22 h à 22 h 59 **et** de 18 h à 20 h 59". The two
|
|
266
|
+
consecutive "et" at the step/range join can momentarily read as one chained
|
|
267
|
+
range (hour 18 is covered by both arms — the same overlap behind the es+pt
|
|
268
|
+
hour-window-overlap residual on this exact cron). Fire set is correct;
|
|
269
|
+
meaning-preserving. A clean fix collapses the overlapping step/range arms to
|
|
270
|
+
the hour union, which is a change to the **shared es-derived rendering**, not
|
|
271
|
+
fr-only — tracked as a joint es+fr (and es+pt) follow-up in docs/backlog.md
|
|
272
|
+
(per-language follow-ups). Left as-is in this corpus.
|
|
273
|
+
|
|
274
|
+
## Known trade-offs
|
|
275
|
+
|
|
276
|
+
- `short` only switches spelled numbers to digits; fr name abbreviations
|
|
277
|
+
(lun., janv.) are not yet implemented (same residue as es/pt).
|
|
278
|
+
- The spaced-`h` clock and the per-value "1er" are correctness/register-critical;
|
|
279
|
+
the renderer forms both programmatically rather than hard-coding strings.
|
|
280
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "French",
|
|
3
|
+
"status": "beta",
|
|
4
|
+
"humanReview": null,
|
|
5
|
+
"modelReview": "sibling-derived from es (the validated Romance sibling): the es renderer's structure was ported (plan override, OR-union frame, parity predicates, re-strategies, dialect scaffold) and its lexicon translated to fr-FR, then TDD'd to green against the reviewed fr-FR corpus. The corpus was a candidate translated from the reviewed es corpus and finalized by a blind 3-persona fr-FR Sonnet panel (everyday / copy-editor / technical, 2026-06-27) before the port (corpus -> review -> port; tooling/docs/language-pipeline.md). The es->fr gap is wider than es->pt (a deliberate stress test): the divergences the panel and TDD surfaced and the renderer now handles are three net-new layers plus a clock rewrite — (1) the 'à 9 h 30' 24-hour clock (no article, unpadded, spaced typographic 'h', minuit/midi for the exact 0:00/12:00 point only, the seconds-clock 'H h MM min SS s' with the zero-minute suppressed; the es 12-hour day-period machinery dropped and {ampm} accepted as a documented no-op); (2) preposition+article contraction (de+le=du, de+les=des, a+le=au, a+les=aux; de la / a la / l' unfused) on date/scope nouns; (3) the per-VALUE 'le 1er' ordinal (the 1st only; every other day the bare cardinal 'le N'), carried into ranges, lists, and OR-union date arms; (4) gender agreement (masculine weekdays/months, gendered Quartz nth ordinals, the agreeing cadence determiner toutes les heures / tous les mois). Plus 'le lundi' singular-definite recurrence (multi-day lists singular-definite, 'du lundi au vendredi' ranges), the 'soit X soit Y' inclusive-union frame with 'n'importe quel' weekday arms, 'ouvrable' for the W operator, and no comma before 'et'. Objective gates (round-trip orchestrator-run, fuzz dropped-value detector, both-side OR-scope, cRonstrue fr reference) plus the panel gate beta.",
|
|
6
|
+
"note": "BETA — model-validated: sibling-derived from es (a proven structure/style anchor), TDD-green over a blind-panel-reviewed fr-FR corpus, and clean on the corpus-independent mechanical gates (fuzz, conciseness, cRonstrue fr). One structural residual is inherited unchanged from the es donor and is NOT an fr regression (tracked as a joint es+fr/es+pt follow-up; notes.md §Residual): the double-'et' boundary on '* 2/4,18-20 * * *' where a step segment's per-hour windows join the range window with a second 'et' (fire set correct, meaning-preserving). The 'short' style switches spelled numbers to digits but fr name abbreviations (lun., janv.) are not yet implemented (same residue as es/pt). Graduates to stable only on fluent-fr human review. fr-CA (OQLF / Canadian French — a non-spaced or differently-spaced 'h', divergent date register) is a future dialect axis (notes.md §Dialect axis); no regional dialect ships yet, and the colloquial unspaced '9h30' clock is an opt-in custom style.",
|
|
7
|
+
"dialects": {}
|
|
8
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Portuguese dialect style tables. Dialect names are language-scoped; the
|
|
2
|
+
// default `pt` style is anchored to the Brazilian norm (VOLP / Academia
|
|
3
|
+
// Brasileira de Letras, plus cronstrue `pt_BR`); see notes.md. Custom objects
|
|
4
|
+
// merge over the `pt` defaults. pt-PT is a future dialect axis (notes.md
|
|
5
|
+
// §"Dialect axis"); no regional dialect ships yet.
|
|
6
|
+
import type {Cronli5Options} from '../../types.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Portuguese's own resolved style shape has a separator,
|
|
10
|
+
* clock default, meridiem form, and `h` suffix.
|
|
11
|
+
*/
|
|
12
|
+
export interface PortugueseStyle {
|
|
13
|
+
// Clock default: false renders 24-hour times (pt-BR norm), true renders
|
|
14
|
+
// 12-hour day-period times. An explicit `{ampm}` option still overrides this.
|
|
15
|
+
ampm: boolean;
|
|
16
|
+
// Append an "h" after a clock time ("às 09:00 h"). Opt-in only — the "h"
|
|
17
|
+
// register reads colloquial/formal in pt-BR, deferred per notes.md.
|
|
18
|
+
hSuffix: boolean;
|
|
19
|
+
// How a 12-hour time names its half of the day: 'descriptors' for the
|
|
20
|
+
// pt-BR "da madrugada/manhã/tarde/noite", 'english' for the AM/PM meridiem.
|
|
21
|
+
// No shipped dialect uses 'english'; kept for parity with the donor scaffold.
|
|
22
|
+
meridiem: 'descriptors' | 'english';
|
|
23
|
+
// Separator between hours, minutes, and seconds. The colon is the pt-BR
|
|
24
|
+
// default; a custom style can opt into the period.
|
|
25
|
+
sep: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// The pt-BR default: 24-hour, colon.
|
|
29
|
+
const pt: PortugueseStyle = {
|
|
30
|
+
ampm: false,
|
|
31
|
+
hSuffix: false,
|
|
32
|
+
meridiem: 'descriptors',
|
|
33
|
+
sep: ':'
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// One `pt` table today = pt-BR. pt-PT is a future dialect axis (notes.md);
|
|
37
|
+
// it would clear its own native panel before shipping, so it is not declared
|
|
38
|
+
// here yet.
|
|
39
|
+
const dialects: {[name: string]: PortugueseStyle} = {
|
|
40
|
+
pt,
|
|
41
|
+
// Brazil is the default; named explicitly so it is a recognized choice and
|
|
42
|
+
// has a home if it ever diverges.
|
|
43
|
+
'pt-BR': pt
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Resolve the `dialect` option to a style table.
|
|
47
|
+
function resolveDialect(dialect: Cronli5Options['dialect']): PortugueseStyle {
|
|
48
|
+
if (typeof dialect === 'object' && dialect !== null) {
|
|
49
|
+
return {...dialects.pt, ...dialect};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// A string dialect indexes the table; unknown names fall back to `pt`.
|
|
53
|
+
return dialects[dialect as string] || dialects.pt;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export {resolveDialect};
|