# Address Formatting

[![Build Status](https://github.com/OpenCageData/address-formatting/actions/workflows/ci.yml/badge.svg)](https://github.com/OpenCageData/address-formatting/actions/workflows/ci.yml)

Templates and test cases for address formats used in territories around the world. The templates can be processed in any programming language ([see list of processors](#processing-logic)).

## Example

Given a set of address parts:

```yaml
house_number:  17
road:          Rue du Médecin-Colonel Calbairac
neighbourhood: Lafourguette
suburb:        Toulouse Ouest
postcode:      31000
city:          Toulouse
county:        Toulouse
state:         Midi-Pyrénées
country:       France
country_code:  FR
```

We want to compile an address in the format consumers expect:

```
17 Rue du Médecin-Colonel Calbairac
31000 Toulouse
France
```

## Why Use This?

The intended use case is database or geocoding systems (forward, reverse, autocomplete) where we know both the country of the address and the language of the user/reader. The address is displayed to a consumer (for example in an app) and not used to print on an envelope for actual postal delivery. We use it to format output from the [OpenCage Geocoding API](https://opencagedata.com/api).

## Scope

**What we handle:**
- Incomplete data
- Anything with a name (peaks, bridges, bus stops)

**What we don't handle** (unlike [physical postal mail](http://www.bitboost.com/ref/international-address-formats.html)):
- Apartment/flat numbers, floor numbers
- PO boxes
- Translating the destination address language (whatever language is input is output)

## Processing Logic

Our goal is a series of programming language-independent templates that can be processed by whatever software you like.

### Open-Source Implementations

| Language | Repository | Notes |
|----------|------------|-------|
| Android | [AndroidAddressFormatter](https://github.com/woheller69/AndroidAddressFormatter) | |
| Elixir | [ex_address_formatting](https://github.com/dkuku/ex_address_formatting) | |
| Go | [address-formatter](https://github.com/timonmasberg/address-formatter) | |
| Java | [address-formatter-java](https://github.com/placemarkt/address-formatter-java) | |
| JavaScript | [address-formatter](https://github.com/fragaria/address-formatter) | |
| Kotlin | [address-formatter-kotlin](https://github.com/bettermile/address-formatter-kotlin) | |
| Perl | [Geo-Address-Formatter](https://metacpan.org/release/Geo-Address-Formatter) | |
| PHP | [address-formatter-php](https://github.com/predicthq/address-formatter-php) | |
| PowerShell | [AddressFormatter](https://github.com/GruberMarkus/AddressFormatter) | Cross-platform |
| Python | [addressformatting](https://github.com/pudo/addressformatting/tree/master) | No longer maintained |
| Ruby | [address_composer](https://github.com/mirubiri/address_composer) | |
| Rust | [address-formatter-rs](https://github.com/antoine-de/address-formatter-rs) | No longer maintained |
| Scala | [address-formatter](https://github.com/ben-willis/address-formatter) | |

We welcome more language implementations. The more people who use the templates, the more likely bugs will be reported.

**If you write a processor**, please submit a pull request adding it to the list. Include this repo as a [git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) so we all use the same templates/configuration and stay in sync. See [how we do it in the Perl parser](https://github.com/OpenCageData/perl-Geo-Address-Formatter/blob/master/README.md#installation) for an example.

## International Coverage

As of March 2024:

| Metric | Count |
|--------|-------|
| Known territories | 251 |
| Territories with tests | 251 (100%) |
| Territories with rules | 251 (100%) |
| Territories without rules or tests | 0 (0%) |

This output is generated by `bin/coverage.pl`. Run `bin/coverage.pl -d` for a detailed breakdown.

The list of all known territories is in `conf/country_codes.yaml`.

> **Note:** The list contains all officially assigned [ISO 3166-1 alpha-2 codes](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements). This is not a political statement about the status of any territory.

**We need more language-specific abbreviations.** See `conf/abbreviations`. Pull requests welcome!

## File Format

- **Configuration:** [YAML](http://yaml.org/) format
- **Templates:** [Mustache](http://mustache.github.io/) with one variation: `{#first}` sections take the first alternative for which a variable could be interpolated

Both formats are human-readable, strict, handle escaping, and support comments. YAML allows references ("anchors") to avoid duplication; Mustache allows sub-templates ("partials").

## How to Add Your Country/Territory

### Step 1: Create Test Cases

Edit the `.yaml` testcase for the country/territory in `testcases/countries`. File names correspond to [ISO 3166-1 alpha-2 codes](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) (see `conf/country_codes.yaml`).

**To get sample data:**
1. Find an addressed location (house, business, etc.) in your target territory on [OpenStreetMap](https://www.openstreetmap.org)
2. Get the coordinates (lat, long)
3. Enter the coordinates into the [OpenCage Geocoding API demo](https://opencagedata.com/demo)
4. Check the resulting JSON in the *Raw Response* tab

### Step 2: Define Formatting Rules

Edit `conf/countries/worldwide.yaml`:

- **If your territory uses an existing generic format** (defined at the top of the file): map your `country_code` to the generic template. You may still want to add cleanup code (see the `DE` entry as an example).
- **If not**: define a new rule set (which may or may not be generic). You may also need to define new state/region mappings in `conf/state_codes.yaml`.

### Step 3: Test

Process the `.yaml` test via a processor (see above) and verify the input produces the desired output. We run these checks automatically against pull requests to prevent regressions.

**Questions?** Submit an issue.

## Formatting Rules

| Rule | Description |
|------|-------------|
| `replace:` | Regex operating on input values. Useful for removing bureaucratic cruft like "London Borough of". Prefix with `key=` (e.g., `city=`) to operate only on that key. |
| `postformat_replace:` | Regex operating on the final output. |
| `add_component:` | Add a component with format `component=XXXX`. |
| `change_country:` | Change the country value of the input. Useful for dependent territories. Supports substitutions like `$state`. See `testcases/countries/sh.yaml` for an example. |
| `use_country:` | Use the formatting configuration of another country. Useful for dependent territories to avoid duplicating configuration. |

## Roadmap

More tests are always needed. For every rule about addresses there are exceptions and edge cases.

**Planned features:**
- Basic error checking (e.g., ignore values that obviously cannot be postcodes)
- Rules for postcode format validation

We welcome your pull requests. Together we can address the world!

## License

MIT License - see [LICENSE.txt](LICENSE.txt) for details.

## Resources

### Testing Data

[Lists of random addresses/postcodes/coordinates](https://opencagedata.com/tools/address-lists) for testing (general or country-specific).

### Further Reading

- [Our blog post announcing this project](https://blog.opencagedata.com/post/99059889253/good-looking-addresses-solving-the-berlin-berlin) and the motivations behind it
- [Falsehoods Programmers Believe about Addresses](http://www.mjt.me.uk/posts/falsehoods-programmers-believe-about-addresses/) by Michael Tandy

### Related Projects

- [OpenStreetMap](https://www.openstreetmap.org) - Open address data
- [OpenAddresses](http://openaddresses.io/) - Open address data
- [OpenCage Geocoder](https://opencagedata.com) - Convert coordinates to formatted addresses
- [what3words](http://what3words.com) - An alternative to traditional addresses

---

## About OpenCage GmbH

<a href="https://opencagedata.com"><img src="opencage_logo_300_150.png" alt="OpenCage logo"></a>

We run a worldwide [geocoding API](https://opencagedata.com/api) and [geosearch](https://opencagedata.com/geosearch) service based on open data. [Learn more about us](https://opencagedata.com/about).

We also organize [Geomob](https://thegeomob.com), a series of regular meetups for location-based service creators. If you like geo stuff, check out [the Geomob podcast](https://thegeomob.com/podcast/).