Observation recorded while implementing #4028 (PR: claude/issue-4028-addressfield-locale-remainder). Not a regression — #4028 is a strict improvement over the hardcoded US order — this records the limit that fix had to accept, so the next reader re-judges it instead of inheriting it.
formatAddress(addr, locale) now orders the parts by the reader's display locale (useDisplayLocale()): zh / ja / ko read largest-first, every other locale keeps the small-to-large order. Both callers — AddressField's readonly branch and AddressCellRenderer — pass the same locale, so the two surfaces objectui#4037 unified stay in step.
The limit
An address format is a property of where the mail goes, not of who is reading. A zh console showing a US customer's address renders it largest-first; a US console showing a Chinese address renders it street-first. Both are wrong for the address and right for the reader, and a CRM is exactly where cross-border addresses are normal.
Why the better channel was not taken in #4028
AddressValue (from @objectstack/spec/data) carries countryCode, which would be the correct, unambiguous input for a per-country rule. Measured on main today: AddressField exposes no countryCode input and never writes one — it renders five boxes (street / city / state / postalCode / country) and handleFieldChange only preserves whatever countryCode a producer already stored. So on data this widget produces, that channel is empty, and an address-driven rule would have had nothing to read for the majority of records. The remaining alternative — matching the free-text country string ("中国" / "China" / "PRC" / "美国") — is a name-guessing table with no source of truth behind it.
What a fix would need
- A channel that actually carries the country: either a
countryCode input/derivation in AddressField, or a producer-side guarantee that stored addresses carry it.
- A per-country order rule (CLDR / libaddressinput-shaped) replacing the current language-subtag set in
packages/fields/src/widgets/address-format.ts, with the reader's locale kept only as the fallback for an address that states no country.
Note (2) also covers a second real case the current two-profile rule does not: de / fr / es write the postal code before the city ("10115 Berlin"), which today renders as a separate comma group.
The pin that must move if this is taken: orders a US address by the READER, not by the address — the stated limit in packages/fields/src/widgets/__tests__/AddressField.i18n.test.tsx. It was written to make this decision visible rather than silent.
Observation recorded while implementing #4028 (PR:
claude/issue-4028-addressfield-locale-remainder). Not a regression — #4028 is a strict improvement over the hardcoded US order — this records the limit that fix had to accept, so the next reader re-judges it instead of inheriting it.What #4028 does
formatAddress(addr, locale)now orders the parts by the reader's display locale (useDisplayLocale()):zh/ja/koread largest-first, every other locale keeps the small-to-large order. Both callers —AddressField's readonly branch andAddressCellRenderer— pass the same locale, so the two surfaces objectui#4037 unified stay in step.The limit
An address format is a property of where the mail goes, not of who is reading. A
zhconsole showing a US customer's address renders it largest-first; a US console showing a Chinese address renders it street-first. Both are wrong for the address and right for the reader, and a CRM is exactly where cross-border addresses are normal.Why the better channel was not taken in #4028
AddressValue(from@objectstack/spec/data) carriescountryCode, which would be the correct, unambiguous input for a per-country rule. Measured onmaintoday:AddressFieldexposes nocountryCodeinput and never writes one — it renders five boxes (street / city / state / postalCode / country) andhandleFieldChangeonly preserves whatevercountryCodea producer already stored. So on data this widget produces, that channel is empty, and an address-driven rule would have had nothing to read for the majority of records. The remaining alternative — matching the free-textcountrystring ("中国" / "China" / "PRC" / "美国") — is a name-guessing table with no source of truth behind it.What a fix would need
countryCodeinput/derivation inAddressField, or a producer-side guarantee that stored addresses carry it.packages/fields/src/widgets/address-format.ts, with the reader's locale kept only as the fallback for an address that states no country.Note (2) also covers a second real case the current two-profile rule does not:
de/fr/eswrite the postal code before the city ("10115 Berlin"), which today renders as a separate comma group.The pin that must move if this is taken:
orders a US address by the READER, not by the address — the stated limitinpackages/fields/src/widgets/__tests__/AddressField.i18n.test.tsx. It was written to make this decision visible rather than silent.