# Names, addresses and countries

These types hold people's names, postal addresses and email addresses,
and the closed lists every app needs: countries and their subdivisions,
languages, scripts, continents, nationalities. Each list is a built-in
enum, read by its code and shown by its name in the reader's language.

Here a shop sends parcels abroad and prints each label for the person who
packs it, greets its customers by name, and reads the countries a
supplier's file gives by their three-letter codes:

```jadpo
type FileText = Text {}

type UnknownCountry = Failure {
    kind: FailureKind.invalid_value
}

/// A parcel's label, as its packer reads an address: in the order of the
/// parcel's country for the packer's language.
function parcel_label(parcel_to: PostalAddress, packer: Locale) -> PresentationText {
    return text.format(parcel_to, locale: packer)
}

/// A customer's name as a greeting writes it in the reader's language.
function greeting_name(customer: PersonName, viewer: Locale) -> PresentationText {
    return text.format(customer, locale: viewer)
}

/// The country a supplier's file gives by its three-letter code.
function supplier_country(raw: FileText) fails UnknownCountry -> Country {
    var made_in: Country = attempt text.parse(raw, as: Country, form: CodeForm.alpha3) {
        malformed: UnknownCountry
    }
    return made_in
}
```

```jadpo
test "an address is in its country's order for the reader" {
    var jp_parcel: PostalAddress = PostalAddress {
        lines: ["丸の内2-7-2"]
        locality: "千代田区"
        region: "東京都"
        postal_code: "100-8994"
        country: Country.jp
    }
    assert parcel_label(jp_parcel, Locale.ja_jp) == "〒100-8994\n東京都千代田区\n丸の内2-7-2"
    assert parcel_label(jp_parcel, Locale.en_gb) == "丸の内2-7-2\n千代田区, 東京都\n100-8994\nJapan"
    var de_parcel: PostalAddress = PostalAddress {
        lines: ["Platz der Republik 1"]
        locality: "Berlin"
        region: none
        postal_code: "11011"
        country: Country.de
    }
    assert parcel_label(de_parcel, Locale.de_de) == "Platz der Republik 1\n11011 Berlin"
    assert parcel_label(de_parcel, Locale.en_us) == "Platz der Republik 1\n11011 Berlin\nGermany"
}

test "a name is in the order the reader's language gives" {
    var countess: PersonName = PersonName {
        given: "Ada"
        family: "Lovelace"
    }
    assert greeting_name(countess, Locale.en_gb) == "Ada Lovelace"
    assert greeting_name(countess, Locale.hu_hu) == "Lovelace Ada"
    var yamada: PersonName = PersonName {
        given: "太郎"
        family: "山田"
    }
    assert greeting_name(yamada, Locale.en_gb) == "山田太郎"
}

test "a closed list reads its codes and its other code systems" {
    var made_in: Country = attempt supplier_country("gbr")
    assert made_in == Country.gb
    assert supplier_country("UK") fails UnknownCountry
    assert text.from(Country.in_) == "IN"
    assert text.from(Country.gb, form: CodeForm.numeric) == "826"
    assert text.format(Country.gb, locale: Locale.fr_fr) == "Royaume-Uni"
    assert text.format(Language.ja, locale: Locale.en_gb) == "Japanese"
    assert list.count(Country.all) == 249
}
```

A name and an address are stored as a column per part, so a query can
read a town or a country as it reads any column. They come back as they
were written:

```jadpo
/// Someone the mailroom sends parcels to.
entity Recipient {
    id: Uuid
    name: PersonName  // columns `name_given` and `name_family`
    ship_to: PostalAddress  // a column per part, its lines a `text[]`
    citizen_of: Nationality?  // its code, `IN`
}
```

```jadpo
use mailroom/entities { Recipient }
use mailroom/actions { readdress }

test "a name and an address are kept part by part" {
    var enrolled: Recipient = enrol(
        PersonName {
            given: "Mary"
            family: none
        },
        PostalAddress {
            lines: ["Flat 2", "14 Rue de Rivoli"]
            locality: "Paris"
            region: none
            postal_code: "75004"
            country: Country.fr
        },
        Nationality.in_
    )
    var kept_record: Recipient = attempt Recipient.get(enrolled.id)
    assert kept_record.name == enrolled.name
    assert kept_record.ship_to == enrolled.ship_to
    assert kept_record.citizen_of == Nationality.in_
    var readdressed: Recipient = readdress(
        kept_record,
        PostalAddress {
            lines: ["2-7-2 Marunouchi"]
            locality: "Chiyoda-ku"
            region: "Tokyo"
            postal_code: "100-8994"
            country: Country.jp
        }
    )
    var kept_after: Recipient = attempt Recipient.get(enrolled.id)
    assert kept_after.ship_to == readdressed.ship_to
}
```

## The lists, and where they come from

Each list is shipped with the release, generated once into
`jadpo_std::standard::places_data`:

- `Country` is ISO 3166-1's 249 assigned codes as CLDR 48.2 carries them:
  its regular regions, less the exceptionally reserved `AC`, `CP`, `CQ`,
  `DG`, `EA`, `IC` and `TA` and the user-assigned `XK`. Its alpha-3 and
  numeric codes come from CLDR's code mappings. Its retired codes are the
  eleven ISO 3166-3 codes CLDR marks deprecated (`AN`, `BU`, `CS`, `DD`,
  `FX`, `NT`, `SU`, `TP`, `YD`, `YU`, `ZR`), shown by ISO 3166-3's English
  names, because CLDR names none of them.
- `CountrySubdivision` is ISO 3166-2 as CLDR 48.2 carries it: 5,027
  codes, with the 626 that CLDR marks deprecated retired.
- `Language` is ISO 639-1's 183 codes, from the Library of Congress's ISO
  639-2 list (2026-10-18). Its alpha-3 form is ISO 639-2/T. `in`, `iw`,
  `ji`, `jw` and `mo` are retired. A language with only a three-letter
  code isn't in the list.
- `WritingScript` is ISO 15924's 224 codes (2026-10-18), less the
  private-use range. Its numeric form is ISO 15924's number.
- `Continent` is UN M49's five regions, and Antarctica, which M49 lists
  beside them. It follows the authority's list, so the Americas are one
  region. Its codes are digits, so each variant's code is its own name, as
  a `Weekday`'s is, and M49's number is its numeric form.
- `TextDirection` is the enum `locale.direction` gives. Its codes are
  `ltr` and `rtl`, as HTML's `dir` attribute writes them.
- `Nationality` takes `Country`'s codes and names, as a passport names a
  nationality by its country. CLDR has no names for nationalities, and
  ICAO's codes for travel documents belong to `IdentityDocument`.
- `Currency` also holds the six codes ISO 4217 has withdrawn since 2021
  (`ANG`, `BGN`, `CUC`, `HRK`, `SLL`, `ZWL`), retired.

## Closed lists are enums

A standard type is a built-in enum when its list is closed, published by
an authority and rarely changes, as `Locale`, `Zone` and `Weekday` are. A
huge or fast-changing list stays text with a `pattern:`, and is checked
against its list where this page says so: `IataAirport`, `PostalCode`,
`HsCode`, `UnLocode`, `Icd10Code`, `SpdxLicence`, `Emoji`.

- `HttpStatus` stays a number, because its registry is open by design: an
  unknown code is still a status.
- `CallingCode` stays text, because its codes are digits.
- `CountrySubdivision`, about 5,000 codes with a few changes a year, is
  an enum. Validating an address needs the list anyway, CLDR names each
  subdivision, and its changes arrive with releases, as the time-zone
  database's do.

Jadpo doesn't match names such as "England" or "UK" to codes, in
`text.parse` or in any operation. Which name means which code is a
judgement, and it differs by locale: England is
`CountrySubdivision.gb_eng`, not a country. A dropdown built from `.all`
already asks a person to pick. A package can offer name matching if an
import needs it.

## Variants and `.all`

A variant is its code in lower case with `_` for `-`:
`CountrySubdivision.gb_eng`, as `Locale` writes its variants. A code that
is a [reserved word](naming.md), or is
`all`, takes a trailing `_`: `Country.in_`, `Language.or_`.

**`.all`** lists an enum's variants in its order, a closed list's by
code, as a `List` of the enum. Retired codes are left out, because a
dropdown never offers Yugoslavia. On an enum whose variants carry data,
`.all` is `type.unknown_enum_variant`, and a variant called `all` is
`type.reserved_variant_name`.

## Retired codes

A retired code is refused where code writes it as a value, comparisons
included: `type.variant_retired`. You can decline it, and the decline's
reason says why the code still needs the old value. A `match` arm on a
retired code isn't refused, because it only reads, and a `match` lists
every variant anyway ([Match](match.md)).

## Reading codes: `form:`

`form:` picks which of a list's code systems to read or write, as a
`CodeForm` written out: `CodeForm.alpha3`, `CodeForm.numeric`. A value
held in a variable is `type.mismatch`. A form the type has no codes in is
`type.temporal_option_unused`, as an option that means nothing for a type
is.

`text.parse` reads a form's code ignoring case. Where a retired variant
shares a code with a current one, the code reads as the current one:
`ind` is `Language.id`, not `Language.in_`.

At a boundary, such as a request, JSON, a CSV cell or a setting, a
standard enum's code is read ignoring case and written back in its
canonical case. No two codes in these lists differ only by case, so that
is safe. An unknown code is `invalid_value` naming the field, with the
rule `variant`. Spaces and other spellings are still refused.

## Stored and shown

**Stored:** a closed list's column holds its code, as `Currency`'s does:
`GB`, and `IN` rather than `in_`. An object is stored as a column per
part, named `<field>_<part>`. A part that may be `none` is a column that
may be `NULL`. A `PostalAddress`'s lines are a `text[]` column, and its
country is its code.

**Shown:** a country, a continent, a language and a script by CLDR's name
in the reader's locale, through ICU4X's display names, and a withdrawn
code by its list's English name. A subdivision is shown by CLDR's English
name in every locale, because CLDR names few in other languages and
ICU4X doesn't include them. A continent is shown by CLDR's name for its
M49 region, and Antarctica by its territory's name.

## People's names

**`PersonName`** is a given name and, where the person has one, a family
name. A single name is a given name, as CLDR holds a mononym, so no rule
across the fields is needed. It has no single text form, and its JSON is
its parts.

`text.format` applies CLDR's medium, formal pattern for referring to a
person, from CLDR 48.2's person-name data:

- the name's locale is the reader's when the name is in the reader's
  script; otherwise it is the likeliest locale for the name's script,
  which then formats it;
- the reader's locale's lists give the order of the parts;
- the name's locale gives the space between them, none in Japanese.

Jadpo applies the patterns itself. ICU4X's formatter is experimental,
leaves a pattern's literals behind ("Ada Lovelace ,") and doesn't work out
the order.

## Addresses

**`PostalAddress`** is its lines as written, then a locality, a region
and a postal code, each one line that may be `none`, and its `Country`.
The region is text as the envelope writes it, not a
`CountrySubdivision`. A dependent locality or a sorting code goes in the
lines, and the recipient's name is your application's own field.

CLDR has no address formats, so `text.format` uses libaddressinput's,
Google's formats from UPU data (Apache-2.0, commit `81eb962` of
2026-05-15):

- a reader of one of the country's languages gets the country's own
  format, and anyone else its Latin-script format;
- the text between two fields appears only where the fields beside it
  are there;
- the country's CLDR name comes last for a reader in another country.

Nothing is put in capitals. UPU's capitals are for the envelope, not for
people reading.

**`EmailWithName`** is a name that may be `none` and an `Email`. Its
canonical text is RFC 5322's mailbox with RFC 6532's UTF-8:
`Ada Lovelace <ada@example.com>`. The name is quoted only where it must
be, and an address with no name is written alone. Only that text is
read. Its JSON is its parts.

## Lines and other text

**A line**, an object's part of text, is 1 to 200 characters with no
control character and no space at either end, as an invoice number is. A
literal is checked while compiling.

These are held as text:

- `GivenName` and `FamilyName` are a line;
- `Username` is `[a-z0-9_.-]{1,64}`;
- `PhoneExtension` is up to 20 digits, the longest libphonenumber reads
  after `ext.`;
- `Uprn`, a UK property's unique reference number, is 1 to 12 digits with
  no leading zero, held as text so that no digit is lost.

## Personal data and shapes

**Personal data:** a name, a given or family name and a username are
`identifying`; a mailbox, an address, an extension and a UPRN are
`contact`; a nationality is `demographic`. The lists of places aren't
personal data.

**No shapes:** `type.standard_exists` matches none of the objects,
because `{ name, email }` and `{ first_name, last_name }` are too common
in applications' own types to be a sign that a type is one.
