Skip to content

Resolve localized formats and names per locale - #4

Merged
yogthos merged 4 commits into
mainfrom
feat/locale-formatting
Aug 1, 2026
Merged

Resolve localized formats and names per locale#4
yogthos merged 4 commits into
mainfrom
feat/locale-formatting

Conversation

@yogthos

@yogthos yogthos commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Localized DateTimeFormatter patterns and month/day names were a single
hardcoded US-shaped table used for every locale, and names came from libc
strftime, so a French month rendered in English on any machine without
fr_FR installed — a wrong answer that looks like a right one.

jolt.time.locale-data now carries patterns for every (kind, style) pair plus
month and day names for 14 locale ids, and jolt.time.number-data carries
number and currency symbols for 25. Every value is measured from the reference
JVM; the currency entries were verified by rebuilding the JVM's formatted output
from the stored fields.

Also registers all three of jolt core's locale extension points — :date-names,
:number-symbols and :currency-data — which core declares carrying only the
root locale, since it has no locale data of its own. Registration is guarded per
point, so a jolt older than any of them still loads this library and simply keeps
root behaviour there. Needs jolt 0.5.13 for the points to exist.

Resolution narrows (zh-CNzh → root), and an id with no entry lands on
root, matching the JVM. Note ROOT's wide month and day names are the abbreviated
forms — the JVM renders "Mar" for MMMM at root — and a language-only id has no
country and therefore no currency, so de is the generic sign and only de-DE
carries the euro. Both are as measured.

Suite: 99 tests, 1037 assertions, 0 failures.

Yogthos added 4 commits July 31, 2026 16:59
ofLocalizedDate/Time/DateTime ignored the locale: one hardcoded US-shaped
style-patterns map served every locale, and month/day names came from
strftime, which silently renders English on machines without the locale
installed. Patterns and names now come from bundled tables measured from
the reference JVM, with narrowing (zh-CN -> zh) and a ROOT fallback for
unknown or malformed ids; (Locale. "en_US") is a malformed language
subtag and lands on ROOT, not English. ofLocalized* defers pattern
resolution until .withLocale supplies the locale. Also fixes the y
pattern letter to mean the full year (yy stays two-digit), and joins the
two-arg Locale ctor ("de" "DE" -> "de-DE").
The generated tables had ROOT's wide names spelled out (January, Monday)
to make a localized long date read "2014 March 1". CLDR ROOT abbreviates
them — the reference JVM renders "Mar" and "Sat" — so spelling them out
makes MMMM and EEEE at ROOT disagree with the JVM everywhere, to hit a
value nothing needs: Selmer's assertion there accepts either shape and
says so in its own comment.

Restores the measured values and adjusts the one test expectation.
jolt core's java.text.SimpleDateFormat resolves MMM/MMMM/EEE/EEEE through a
:date-names extension point and carries ROOT alone, since it has no locale
data of its own. Hand it the tables this library already bundles so an
explicit locale renders in its own language there too, not only through
DateTimeFormatter.

Guarded on the point existing, so a jolt older than it still loads and just
keeps ROOT names. The guard swallows the one "not declared" case and
rethrows anything else, so a real error in a provider still surfaces.
jolt core declares :number-symbols (String/format's separators) and
:currency-data (NumberFormat/getCurrencyInstance) carrying ROOT alone,
since it has no locale data. number-data holds both tables for 25 locale
ids, measured from the reference JVM and verified by rebuilding the JVM's
formatted output from the fields.

Registration is now one helper across all three points, each skipped
individually if this jolt does not declare it, so the library keeps
loading against an older runtime.

Note a language-only id has no country and so no currency: the JVM renders
de as the generic sign and only de-DE carries the euro. Both are in the
table, as measured.
@yogthos
yogthos merged commit 2738160 into main Aug 1, 2026
2 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