Skip to content

Add multilingual cron descriptions - #1

Merged
alexskinner merged 1 commit into
stablefrom
feature/i18n-descriptions
Sep 3, 2026
Merged

alexskinner merged 1 commit into
stablefrom
feature/i18n-descriptions

Conversation

@alexskinner

Copy link
Copy Markdown
Collaborator

The cron-utils JAR this library replaces described crons in 16 languages via a Java ResourceBundle. That was lost in the port: describeCronTabExression() still took a locale but ignored it, and every description was a hard-coded English literal.

Descriptions are now built from message templates in i18n/.json, one file per language, shipping the same 17 languages the JAR supported.

Unlike cron-utils, which translated ~26 short atoms and concatenated them in a word order fixed by Java, each template here is a whole phrase, so a language controls its own word order -- month_day is "{1} {2}" in English and Japanese but "{2} de {1}" in Spanish.

Locale resolution mirrors ResourceBundle: pt-BR is served by en.json, then pt.json, then pt_BR.json, so a partial translation degrades one string at a time. Both pt-BR and pt_BR are accepted.

Month names, day names and ordinals are shipped as bundle data rather than read from the engine. LSDateFormat() does not exist on every CFML engine (BoxLang, which the test suite runs on, has no such function), and shipping them keeps output identical everywhere while putting every visible string in front of the translator.

Ordinal overrides are keyed on the exact day of the month and are the one key never inherited from the base bundle. Ordinal rules do not transfer between languages: English marks 1, 21 and 31 alike, French marks only the 1st, so inheriting would render "le 21er".

Also fixes two description bugs found while restructuring:

  • a daily task at a time other than the hour, e.g. "0 30 9 * * *", fell through to the raw-cron fallback instead of "every day at 09:30". The fixed-time-of-day branch required a zero minute; the wildcard-hour branch above it already claims every ambiguous case, so the check was redundant.
  • a stepped hour field with no dedicated case, e.g. "0 0 */5 * * *", was described as "every 5 minutes". The interval ladder is now driven by the field being described rather than by guessing the unit from its bound.

TRANSLATIONS.md explains how to review a language or add one, and docs/translations/.md gives a per-language sheet showing real output for every kind of expression Chrono can describe. Both are generated from the live library by box task run TranslationDocs, so they cannot drift from what the code actually produces.

The cron-utils JAR this library replaces described crons in 16 languages
via a Java ResourceBundle. That was lost in the port: describeCronTabExression()
still took a locale but ignored it, and every description was a hard-coded
English literal.

Descriptions are now built from message templates in i18n/<locale>.json,
one file per language, shipping the same 17 languages the JAR supported.

Unlike cron-utils, which translated ~26 short atoms and concatenated them in
a word order fixed by Java, each template here is a whole phrase, so a
language controls its own word order -- month_day is "{1} {2}" in English
and Japanese but "{2} de {1}" in Spanish.

Locale resolution mirrors ResourceBundle: pt-BR is served by en.json, then
pt.json, then pt_BR.json, so a partial translation degrades one string at a
time. Both pt-BR and pt_BR are accepted.

Month names, day names and ordinals are shipped as bundle data rather than
read from the engine. LSDateFormat() does not exist on every CFML engine
(BoxLang, which the test suite runs on, has no such function), and shipping
them keeps output identical everywhere while putting every visible string in
front of the translator.

Ordinal overrides are keyed on the exact day of the month and are the one
key never inherited from the base bundle. Ordinal rules do not transfer
between languages: English marks 1, 21 and 31 alike, French marks only the
1st, so inheriting would render "le 21er".

Also fixes two description bugs found while restructuring:

* a daily task at a time other than the hour, e.g. "0 30 9 * * *", fell
  through to the raw-cron fallback instead of "every day at 09:30". The
  fixed-time-of-day branch required a zero minute; the wildcard-hour branch
  above it already claims every ambiguous case, so the check was redundant.
* a stepped hour field with no dedicated case, e.g. "0 0 */5 * * *", was
  described as "every 5 minutes". The interval ladder is now driven by the
  field being described rather than by guessing the unit from its bound.

TRANSLATIONS.md explains how to review a language or add one, and
docs/translations/<lang>.md gives a per-language sheet showing real output
for every kind of expression Chrono can describe. Both are generated from
the live library by `box task run TranslationDocs`, so they cannot drift
from what the code actually produces.
@alexskinner alexskinner closed this Sep 3, 2026
@alexskinner alexskinner reopened this Sep 3, 2026
@alexskinner
alexskinner merged commit c837a37 into stable Sep 3, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant