Remove iLib JavaScript interop (flutter_js) entirely and replace with pure Dart implementation.
Reads JSON locale data directly and performs formatting/calculation in Dart.
This section is the single source of truth for the upstream versions. Other docs reference it instead of repeating the numbers, so a version bump only changes the values here (plus the two point-in-time/public spots listed below).
- iLib JS source: v15.0.0 — all
lib/Dart code andtest/cases were converted from the iLib JS at this tag (js/lib/andjs/test/of github.com/iLib-js/iLib;git checkout v15.0.0). - CLDR data: 48.2 — the bundled
assets/locale/JSON (144 locales) was generated from iLib v15.0.0, which incorporates CLDR 46. - When updating to a newer iLib/CLDR: bump the JS source and the generated locale data together (a JS-only or data-only bump will diverge), then re-run the converted tests against the new JS expectations.
- On a version bump, also update these two spots that intentionally state the version
independently (everything else references this section):
README.md— the public version note in the intro.CHANGELOG.md— add a new entry (the existing entries are frozen history; do not edit them).
Pure Dart: each class reads JSON locale data and computes natively (the old flutter_js interop is
gone — see doc/conversion-guide.md for the before/after pattern).
Options → ILibLocaleInfo (determines locale, calendar, clock, meridiems)
→ ILibLoader.getLocaleData(locale) → merged JSON Map
→ extract data via 'ilib.data.{dataType}' key
→ pure Dart calculation
| Class | File | Core Data |
|---|---|---|
| ILibLoader | lib/ilib_init.dart |
JSON load/merge infrastructure |
| ILibLocale | lib/ilib_locale.dart |
BCP-47 parsing |
| ILibLocaleInfo | lib/ilib_localeinfo.dart |
ilib.data.localeinfo |
| ILibCaseMapper | lib/ilib_casemapper.dart |
Dart Map lookups |
| ILibDate | lib/ilib_date.dart |
Date calculation |
| ILibDateFmt | lib/ilib_datefmt.dart |
ilib.data.dateformats + sysres + zoneinfo |
| ILibTimeZone | lib/ilib_timezone.dart |
ilib.data.zoneinfo DST calculation |
| ILibCalendar | lib/ilib_calendar.dart + lib/calendar/ |
Calendar factory + abstract base |
| 9 calendars | lib/calendar/{name}_date.dart + {name}_rata_die.dart |
gregorian, thaisolar, julian, islamic, hebrew, ethiopic, coptic, persian (astronomical), persian-algo (algorithmic) — see doc/date-calendar-architecture.md |
| ILibAstro | lib/calendar/ilib_astro.dart |
Astronomical calculation (ilib.data.astro) |
| ILibDateOptions | lib/ilib_date.dart |
_toCalendarDate() delegates per calendar |
| ILibCurrency | lib/ilib_currency.dart |
Currency metadata lookup (ilib.data.currency) |
| ILibNumFmt | lib/ilib_numfmt.dart |
ilib.data.localeinfo.numfmt + ilib.data.currency |
| ILibScriptInfo | lib/ilib_scriptinfo.dart |
ilib.data.scripts (root-only, locale-independent) |
| ILibCountry | lib/ilib_country.dart |
ilib.data.ctryreverse (code↔name, per-locale) |
| ILibDurationFmt | lib/ilib_durationfmt.dart |
ilib.data.sysres + ilib.data.plurals + ilib.data.dateformats (clock style) — see doc/durationfmt-conversion-plan.md |
See doc/conversion-guide.md for the full checklist (analyze JS source →
verify JSON data → Dart implementation → convert tests → verify), the before/after example, and the
getLocaleData() / _defaultData template. Test setup: setUpAll calls
ILibLoader.instance.loadJSON() (which runs initILib() internally) + loadILibLocaleData('en-US');
pure-calculation classes need no locale loading (see Conventions › Testing).
- Singleton:
ILibLoader.instance loadJSON()— loads current system locale datagetLocaleData(locale)— returns merged locale dataloadILibLocaleData(locale)— loads additional locale- JSON load order (least → most specific, deep-merged; missing files are skipped):
root.json→{lang}.json→ (if script)und-{script}.json→{lang}-{script}.json→ (if region)und-{region}.json→{lang}-{region}.json→ (if script and region){lang}-{script}-{region}.json- All
und-*fallbacks use a hyphen (und-Hans.json,und-CN.json), matching the bundled file naming. - e.g.
en-US:root→en→und-US→en-US - e.g.
zh-Hans-CN:root→zh→und-Hans→zh-Hans→und-CN→zh-CN→zh-Hans-CN - (Implemented in
getJSONDataPaths()inlib/internal/ilib_utils.dart.)
- All
ilib.data.astro— astronomical coefficients (equinox, delta-T, nutation, etc. for Persian astronomical)ilib.data.dateformats— date/time format templates (per calendar)ilib.data.localeinfo— locale metadata (clock, calendar, timezone, digits, etc.)ilib.data.localeinfo.numfmt— number format patterns (nested under localeinfo)ilib.data.plurals— CLDR plural rule tree (used byILibDurationFmtplural selection)ilib.data.scripts— script metadatailib.data.sysres— translated strings (month/day names, AM/PM, etc.)ilib.data.zoneinfo— timezone + DST rules
assets/locale/holds the JSON data files for the full set of supported locales, organized hierarchically and merged at load time. Most-general → most-specific:root.json→{lang}.json→ (if script)und-{script}.json→{lang}-{script}.json→und-{region}.json→{lang}-{region}.json→ (script + region){lang}-{script}-{region}.json(e.g.zh-Hans-CN). Allund-*fallbacks use a hyphen. Multiple files combine to resolve one locale, so the file count (~251) is larger than the locale count. Full order: see Key Infrastructure /getJSONDataPaths().- Version: see Source Versions
lib/
├── flutter_ilib.dart # export hub
├── ilib_init.dart # ILibLoader
├── ilib_locale.dart # BCP-47 locale
├── ilib_localeinfo.dart # locale metadata
├── ilib_date.dart # ILibDateOptions (date options/calculation)
├── ilib_date_accessor.dart # ILibDate interface (year/month/day accessors)
├── ilib_datefmt.dart # date formatting engine
├── ilib_timezone.dart # timezone/DST
├── ilib_calendar.dart # Calendar factory + abstract base
├── ilib_casemapper.dart # case conversion
├── ilib_country.dart # country info (ilib.data.ctryreverse)
├── ilib_numfmt.dart # number format
├── ilib_durationfmt.dart # duration format (sysres + plurals + clock via datefmt)
├── ilib_scriptinfo.dart # script info (ilib.data.scripts)
├── calendar/
│ ├── rata_die.dart # ILibRataDie abstract base (shared static methods)
│ ├── ilib_date.dart # ILibCalendarDate abstract base
│ ├── calendar_utils.dart # mod(), floorDiv() helpers
│ ├── ilib_astro.dart # astronomical calculation utility
│ ├── julian_day.dart # Julian Day helper
│ ├── gregorian_cal.dart # + gregorian_date.dart + greg_rata_die.dart
│ ├── thaisolar_cal.dart # + thaisolar_date.dart + thaisolar_rata_die.dart
│ ├── julian_cal.dart # + julian_date.dart + julian_rata_die.dart
│ ├── islamic_cal.dart # + islamic_date.dart + islamic_rata_die.dart
│ ├── hebrew_cal.dart # + hebrew_date.dart + hebrew_rata_die.dart
│ ├── ethiopic_cal.dart # + ethiopic_date.dart + ethiopic_rata_die.dart
│ ├── coptic_cal.dart # + coptic_date.dart + coptic_rata_die.dart
│ ├── persian_cal.dart # + persian_date.dart + persian_rata_die.dart (astronomical)
│ └── persian_algo_cal.dart # + persian_algo_date.dart + persian_algo_rata_die.dart (algorithmic)
└── internal/
├── ilib_utils.dart # pure stateless helpers: normalizeLocale(), getJSONDataPaths(), isValidLocale(), getSupportedLocales()
├── locale_state.dart # flutter_ilib's library-wide default locale: _currentLocale + currentLocale/getLocale()/setLocale() (owner of the global locale state)
├── plural_utils.dart # getPluralCategory() — CLDR plural-rule eval (used by ILibDurationFmt)
├── math_utils.dart # rounding helpers (used by ILibNumFmt)
└── logger/ # internal logging (LogAdapter + package:logging); not part of the conversion
Editing calendar/date/timezone code? It has feature-specific must-follow rules (pass
offset: tzOffsetDaysto weekday/onOrBefore calls; format calendar-dependent tokens with the formatter's calendar, not_toCalendarDate's gregorian default;'local'= the system timezone; add shared logic to the base class, not the 9 subclasses) — read doc/date-calendar-architecture.md §Critical rules (+calendar-conversion.md,local-timezone-support.md) before changing it.
flutter analyzemust pass for the converted code.- Formatting — run
dart format. The repo is formatted with the short style atpage_width: 80(set inanalysis_options.yaml; short because the package SDK floor is < 3.7). Format-on-save is fine; rundart format .before committing — a committed pre-commit hook enforces this (install once withscripts/install-git-hooks.sh; see doc/development.md › Code Style & Formatting). Switching to the modern "tall" style would require raising the SDK floor to ≥ 3.7 and a one-time repo-wide reformat in its own commit. - Explicit type declarations (
always_specify_types) - Single quotes,
@overrideannotation - No unnecessary comments
- Public classes:
ILibprefix (e.g.,ILibDateFmt) - Calendar implementations: suffix
Cal(e.g.,GregorianCal) - Options classes: suffix
Options(e.g.,ILibDateFmtOptions)
- Test file location: feature-specific folders under
test/ TestWidgetsFlutterBinding.ensureInitialized()is required only when a test loads assets (loadJSON/loadILibLocaleData). Pure calculation classes with no data dependency can be tested without locale loading or the binding (which classes / the calendar-specific exceptions: doc/date-calendar-architecture.md).- Use exact
expect(value, expected)for integer dates, JD round-trips, and epoch subtraction (these produce exact results). ReservecloseTofor calculations with genuine floating-point division (e.g.unixtime / 86400000) — using it on exact values hides precision bugs and weakens the test - Tests converted 1:1 from an iLib JS test go in the main
*_test.dart. Tests with no JS counterpart (Dart-specific: extra accessors like getDayOfYear/getEra,'local'/ system-tz, offset variants) go in a sibling*_extra_test.dart— never add Dart-only cases to the JS-mirrored file (see doc/conversion-guide.md, doc/test-mapping.md) - Do NOT convert JS tests for a locale that flutter_ilib does not support. The authoritative
list of supported locales is
scripts/assemble_ilib/locales.json(the seed used to generateassets/locale/— the 144 bundled iLib locales, see Source Versions); a per-locale test is in scope only if its locale is in that list. When the data is fully absent,ILibLocaleInfo/ILibTimeZone.fromLocalefall back to defaults (e.g.Etc/UTC), so the test cannot reproduce the JS expected value — these are N/A. Do not decide by "the value happens to reproduce": an unsupported locale can still yield the JS value by language fallback, yet it is out of scope — only convert the variant that is inlocales.json. See doc/conversion-guide.md › Test Conversion. - Script-explicit 3-part locale (e.g.
pa-Guru-IN): in scope if its 2-part form (pa-IN) is inlocales.json— same asset files, identical data. N/A if neither form is bundled. - Language-only locale (e.g.
az,pa): in scope if at least one{lang}-*locale is inlocales.json(meaning{lang}.jsonexists). N/A if no{lang}-*is bundled (e.g.ig,lb). - Invisible bidi control chars are valid test data. RTL-locale expected values (e.g.
fa-IR,ar-*) legitimately contain bidi chars like U+202A/U+200E/U+200F — iLib emits them, so keep them verbatim; do not strip or\u-escape.text_direction_code_point_in_literalis set toignoreinanalysis_options.yamlfor this reason — keep it.
- All public classes exported from
lib/flutter_ilib.dart - Add export line when creating new files
For in-depth explanations, see doc/:
- doc/calendar-conversion.md — Cross-calendar conversion logic, JS vs Dart differences
- doc/datefmt-conversion-plan.md — DateFmt implementation details (tokens, templates, DST)
- doc/durationfmt-conversion-plan.md — DurationFmt implementation details (plural-choice engine, clock style, RTL)
- doc/conversion-guide.md — General JS→Dart conversion checklist
- doc/architecture.md — System architecture and data loading
- doc/api.md — Public API reference
- doc/local-timezone-support.md — system
'local'timezone (Strategy A, implemented): the resolved DST/offset behavior, thedstoverlap & spring-forward rules, and the injectable test hooks - doc/date-calendar-architecture.md — calendar/date layer architecture: Rata Die, the calendar type mapping table, constructor pattern, class relationships
- Han Calendar: needs lunar calculations (
_lunarLongitude,_newMoonTime, etc.). Planned as an extension toILibAstro. 'local'IANA zone name (optional): the system-tz behavior is implemented (Strategy A); Strategy B (flutter_timezone) is only needed ifgetId()must return the real zone name (e.g.Asia/Seoul) instead of'local'. See doc/local-timezone-support.md.
Notes on the locale-state design (see lib/internal/locale_state.dart): the single accessor is
currentLocale (the redundant getLocale()/setLocale() wrappers were removed). Optional future
follow-up: consider lib/internal/ → lib/src/ for true path-import privacy. Avoid moving the
state into ILibLoader — it would couple the pure formatters to the singleton. Keep
FlutterILib.locale as the sole public entry point.
flutter test # all tests
flutter test test/calendar/ # one suite (also: date/, root/, number/, basic/)
flutter analyze # static analysisSee doc/development.md › Testing for single-test / -k filter / coverage.