README.md

# Localize.Address

Address parsing and locale-aware formatting for Elixir.

Parses unstructured address strings into structured components using [libpostal](https://github.com/openvenues/libpostal) via NIF, and formats structured addresses into locale-appropriate string representations using templates from the [OpenCageData address-formatting](https://github.com/OpenCageData/address-formatting) project. Supports Unicode-aware capitalization via [Unicode.String](https://hex.pm/packages/unicode_string).

## Installation

Add `localize_address` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:localize_address, "~> 0.1"}
  ]
end
```

### System Dependencies

* **libpostal** is required for address parsing. Install with:

  ```bash
  brew install libpostal    # macOS
  apt-get install libpostal-dev   # Debian/Ubuntu
  ```

* **Address templates** must be downloaded before formatting. Run once after install and whenever upstream templates are updated:

  ```bash
  mix localize.address.download_templates
  ```

## Quick Start

Parse an address, capitalize it, and format for its territory:

```elixir
iex> {:ok, address} = Localize.Address.parse(
...>   "301 Hamilton Avenue, Palo Alto, CA 94303",
...>   capitalize: true
...> )
iex> address.road
"Hamilton Avenue"
iex> address.city
"Palo Alto"

iex> {:ok, formatted} = Localize.Address.to_string(address)
iex> IO.puts(formatted)
301 Hamilton Avenue
Palo Alto, CA 94303
```

Different countries format addresses differently:

```elixir
# United States: house road / city, STATE postcode / country
iex> Localize.Address.to_string(%Localize.Address.Address{
...>   house_number: "1600", road: "Pennsylvania Avenue NW",
...>   city: "Washington", state: "District of Columbia",
...>   postcode: "20500", territory: "United States of America",
...>   territory_code: "US"
...> })
{:ok, "1600 Pennsylvania Avenue NW\nWashington, DC 20500\nUnited States of America"}

# Germany: road number / postcode city / country
iex> Localize.Address.to_string(%Localize.Address.Address{
...>   house_number: "1", road: "Unter den Linden",
...>   city: "Berlin", postcode: "10117",
...>   territory: "Germany", territory_code: "DE"
...> })
{:ok, "Unter den Linden 1\n10117 Berlin\nGermany"}
```

See the [Parsing and Formatting guide](https://hexdocs.pm/localize_address/parsing_and_formatting.html) for detailed usage including territory options, capitalization, and manual struct construction.

## Primary API

* `Localize.Address.parse/2` — parse an unstructured address string into a struct.

* `Localize.Address.to_string/2` — format a struct into a locale-appropriate string.

* `Localize.Address.capitalize/2` — titlecase text fields and uppercase postcodes.

* `Localize.Address.available?/0` — check if the libpostal NIF is loaded.

## Source References

* **[libpostal](https://github.com/openvenues/libpostal)** — C library for parsing and normalizing street addresses using statistical NLP and open geo data. Powers `Localize.Address.parse/2`.

* **[OpenCageData address-formatting](https://github.com/OpenCageData/address-formatting)** — Templates and rules for formatting addresses according to local conventions, covering 267 countries and territories. Powers `Localize.Address.to_string/2`.

* **[Localize](https://hex.pm/packages/localize)** — Provides territory validation, locale resolution, and subdivision name lookups (e.g., "California" ↔ "CA") used for state/county code resolution.

* **[Unicode.String](https://hex.pm/packages/unicode_string)** — Unicode-aware titlecase, used by `Localize.Address.capitalize/2`.

## Conformance

Validated against the full [OpenCageData test suite](https://github.com/OpenCageData/address-formatting/tree/master/testcases) (459 test cases across 251 countries and territories).

**450/459 tests passing (98.0%). 242/251 countries at 100%.**

All major countries pass at 100%: US, GB, DE, FR, CA, AU, IT, ES, IE, JP, SG, IN, BR, NL.

The 9 remaining failures are documented with root causes in the [conformance document](https://hexdocs.pm/localize_address/open_cage_conformance.html).

## License

Apache License 2.0. See [LICENSE.md](https://github.com/elixir-localize/localize_address/blob/v0.1.0/LICENSE.md) for details.