Platform notes¶
The four ports share one API but reach ICU through different doors, so their feature sets differ slightly at the edges. This page is the quick "what can I use where" reference; for the exhaustive matrix, see Feature parity.
The short version¶
| Reaches ICU via | Completeness | |
|---|---|---|
| Java | ICU4J (the reference ICU implementation) | most complete — blocked on nothing |
| Python | PyICU (ICU C++ bindings) | near-complete — three small binding quirks |
| PHP | ext-intl (+ raw ResourceBundle, RBNF) |
v3 reaches everything except likely-subtags & the matcher/index family |
| JavaScript | the standard Intl API |
everything Intl exposes; raw-ICU features omitted by design |
Everything in the Guide without a callout works in all four ports. The exceptions are below.
JavaScript can't do (not in Intl)¶
Intl is a curated formatting subset of ICU — it has no parser, transform
engine, spoof checker, locale matcher, or resource access. Those raw-ICU features
are intentionally omitted, not faked. All are available in PHP, Python, and
Java (unless noted):
spellout()— spell a number out ("one hundred twenty") — needs RBNFordinal()— ordinal text ("1st", "2nd") (the ordinal plural category is available everywhere viapluralCategory())quote()— wrap text in the locale's quotation marks (CLDR delimiter data)get()— raw ICUResourceBundleaccessformatMoment()— arbitrary ICU date/time patternstransliterate()/romanize()— script conversion & ASCII slugs (needsTransliterator)confusable()/suspicious()— spoof detection (needsSpoofChecker)parseNumber()/parseMoney()/parseDate()/parseMoment()—Intlis format-onlybestMatch()/ negotiatingfromAcceptLanguage()— needsLocaleMatcher(Python & Java only)indexBuckets()— needsAlphabeticIndex(Python & Java only)
JavaScript also does not infer a currency from the region: money() requires
an explicit currency code or the currency modifier (PHP, Python, and Java infer
it).
PHP can't do (no ext-intl API)¶
PHP v3 closed most of its old gaps by reconstructing the missing formatters from
CLDR ResourceBundle data (no hardcoded tables) — compact(), relativeDuration(),
numberRange()/moneyRange(), and dateRange() now all work. Two caveats:
dateRange()supportsshort/mediumonly (CLDR ships no long/full interval skeletons thatext-intlcan reach); it throws on long/full.moneyRange()is approximate — it fills the CLDR range pattern but doesn't collapse the shared currency symbol the way native ICU does.
Genuinely still blocked in PHP (no ext-intl API, not cleanly reconstructable
— all available in Python and Java):
addLikelySubtags()/removeLikelySubtags()— no likely-subtags algorithmbestMatch()/ negotiatingfromAcceptLanguage()— noLocaleMatcherbindingindexBuckets()— noAlphabeticIndexbinding
Three Python quirks¶
Python is near-complete, but PyICU 2.16's bindings leave three small gaps (documented in the Python README):
week_info()omits the weekend days. PyICU doesn't bind ICU'sgetDayOfWeekType/isWeekend, and the no-hardcoded-data rule forbids a fallback table — so it returnsfirst_day+minimal_daysonly. (PHP and Java return the weekend too.)relative_duration(numeric="auto")returns the numeric form ("1 day ago", not"yesterday") — PyICU doesn't cleanly expose ICU's "auto" word-forms. The result is always correct, just not colloquial. (PHP and Java produce the words.)supported_values()coverstimeZone/collation/numberingSystem/unit/transliteratorbut notcurrency/calendar.
Java's one exclusive — and what it doesn't block¶
Java (ICU4J) is blocked on nothing: weekend days, auto word forms, and the
full supportedValues set (plus transliterator) all work. It also carries the
one method no other port can reach:
personName()— locale-correct person-name formatting (surname-first locales, initials, formality variants) via ICU 73+'sPersonNameFormatter. JS has only a TC39 proposal,ext-intldoesn't bind it, and PyICU 2.16 doesn't expose it. See Negotiation & names.
Why omit instead of polyfill?¶
Every port follows the same rule: if its runtime's ICU can't produce a result,
the feature is left out — never reimplemented from a hardcoded table. (PHP's v3
CLDR reconstructions are the one nuance — they still read live ICU ResourceBundle
data, so nothing is hardcoded.) That's what keeps all four ports correct and
maintenance-free as CLDR/ICU data evolves, and it's why the gaps above are
by-design boundaries rather than bugs or backlog.