Negotiation & names¶
Pick the best of your supported locales for a user, build alphabetical index buckets for a contact list, and format person names the way each culture expects.
Availability
bestMatch()/ negotiatingfromAcceptLanguage()andindexBuckets()need ICU'sLocaleMatcher/AlphabeticIndex, which only Python and Java bind (PHP'sext-intland JS'sIntlexpose neither).personName()needs ICU 73+'sPersonNameFormatter— Java only across all four ports.
See Platform notes.
Best-match locale negotiation¶
fromAcceptLanguage(header) alone just parses the header. Pass your supported
locales and the port runs CLDR language distance — not crude prefix matching — to
choose the closest one, falling back to the first supported locale if nothing is
close.
// Which of MY locales best serves this user?
new Cosmo("en_AU").bestMatch(List.of("en-US", "en-GB", "fr")); // "en-GB"
new Cosmo("ja").bestMatch(List.of("fr", "de")); // "fr" (fallback)
// Negotiate straight from an Accept-Language header:
Cosmo c = Cosmo.fromAcceptLanguage("fr-CH, en;q=0.9", List.of("en-US", "fr-FR"));
c.locale; // "fr_FR" (public final field)
# Which of MY locales best serves this user?
Cosmo("en_AU").best_match(["en-US", "en-GB", "fr"]) # "en-GB" (closer than en-US)
Cosmo("ja").best_match(["fr", "de"]) # "fr" (fallback: first)
# Negotiate straight from an Accept-Language header:
c = Cosmo.from_accept_language("fr-CH, en;q=0.9", supported=["en-US", "fr-FR"])
c.locale # "fr_FR"
Cosmo.from_accept_language("", supported=["en-US", "fr-FR"]).locale # "en_US"
This is the right tool behind a language switcher: en_AU is served better by
en-GB than en-US; sr-Latn maps to hr over sr-Cyrl. An empty supported
list throws.
Alphabetical index buckets¶
indexBuckets(names) groups strings into the locale's index headers (A–Z, but
가나다 in Korean, あかさ in Japanese, …) with each bucket collation-ordered — the
section headers you'd put down the side of a contact list. Empty buckets are
omitted.
The buckets are returned in index order (an ordered map / LinkedHashMap), so you
can iterate straight into your UI.
Person names (Java only)¶
personName(fields) formats a name with the locale's ordering, spacing, and
script conventions — surname-first where appropriate, no space in CJK, locale-aware
initials. The optional length (short/medium/long) and formality
(formal/informal) refine it. Field keys include given, surname, title,
given2, surname2, generation, credentials, and a special locale key
giving the name's own locale (so a Japanese name renders correctly even from an
en formatter).
import java.util.Map;
// Western order:
new Cosmo("en").personName(Map.of("given", "John", "surname", "Smith"));
// "John Smith"
// Japanese: surname first, no separating space — driven by the name's own locale:
new Cosmo("ja").personName(Map.of(
"given", "太郎", "surname", "山田", "locale", "ja"));
// "山田太郎"
Technology preview
ICU still labels PersonNameFormatter a technology preview, but Cosmo's
personName method surface stays stable regardless — only the underlying
formatting could shift as the CLDR data matures. JavaScript has only a TC39
proposal, and neither ext-intl nor PyICU 2.16 binds the formatter, which is
why this one method is Java-exclusive.