address

package module
Version: v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 8, 2021 License: MIT Imports: 8 Imported by: 0

README

address Build Coverage Status Go Report Card PkgGoDev

Handles address representation, validation and formatting.

Inspired by Google's libaddressinput.

Backstory: https://bojanz.github.io/address-handling-go/

Features

  1. Address struct.
  2. Address formats for ~200 countries.
  3. Regions for ~40 countries, with local names where relevant (e.g: Okinawa / 沖縄県).
  4. Country list, powered by CLDR v40.
  5. HTML formatter.
  6. HTTP handler for serving address formats and regions as JSON: only ~14kb gzipped!

Address struct

Represents an address as commonly handled by web applications and APIs.

type Address struct {
	Line1 string
	Line2 string
	Line3 string
	// Sublocality is the neighborhood/suburb/district.
	Sublocality string
	// Locality is the city/village/post town.
	Locality string
	// Region is the state/province/prefecture.
	// An ISO code is used when available.
	Region string
	// PostalCode is the postal/zip/pin code.
	PostalCode string
	// CountryCode is the two-letter code as defined by CLDR.
	CountryCode string
}

Recipient fields such as FirstName/LastName are not included since they are usually present on the parent (Contact/Customer/User) struct. This allows the package to avoid being opinionated about name handling.

There are three line fields, matching the HTML5 autocomplete spec and many shipping APIs. This leaves enough space for specifying an organization and department, house or hotel name, and other similar "care of" use cases. When mapping to an API that only has two address lines, Line3 can be appended to Line2, separated by a comma.

Address formats

The following information is available:

  • Which fields are used, and in which order.
  • Which fields are required.
  • Labels for the sublocality, locality, region and postal code fields.
  • Regular expression pattern for validating postal codes.
  • Regions and how to display them in an address.

Certain countries (e.g. China, Japan, Russia, Ukraine) have region names defined in both Latin and local scripts. The script is selected based on locale. For example, the "ru" locale will use Russian regions in Cyrilic, while "ru-Latn" and other locales will use the Latin version.

Helpers are provided for validating required fields, regions, postal codes.

Format data was generated from Google's Address Data but isn't automatically regenerated, to allow the community to submit their own corrections directly to the package.

Countries

The country list is auto-generated from CLDR. Updating to the latest CLDR release is always one go generate away.

Most software uses the CLDR country list instead of the ISO one because the CLDR country names match their colloquial usage more closely (e.g. "Russia" instead of "Russian Federation").

To reduce the size of the included data, this package only includes country names in English. Translated country names can be fetched on the frontend via Intl.DisplayNames. Alternatively, one can plug in x/text/language/display by setting a custom CountryMapper on the formatter.

Formatter

Displays an address as HTML, using the country's address format.

The wrapper element ("p") and class ("address") can be configured. The country name can be omitted, for the use case where all addresses belong to the same country.

addr := address.Address{
    Line1:       "1098 Alta Ave",
    Locality:    "Mountain View",
    Region:      "CA",
    PostalCode:  "94043",
    CountryCode: "US",
}
locale := address.NewLocale("en")
formatter := address.NewFormatter(locale)
output := formatter.Format(addr)
// Output:
// <p class="address" translate="no">
// <span class="line1">1098 Alta Ave</span><br>
// <span class="locality">Mountain View</span>, <span class="region">CA</span> <span class="postal-code">94043</span><br>
// <span class="country" data-value="US">United States</span>
// </p>

addr = address.Address{
    Line1:       "幸福中路",
    Sublocality: "新城区",
    Locality:    "西安市",
    Region:      "SN",
    PostalCode:  "710043",
    CountryCode: "CN",
}
locale := address.NewLocale("zh")
formatter := address.NewFormatter(locale)
formatter.NoCountry = true
formatter.WrapperElement = "div"
formatter.WrapperClass = "postal-address"
output := formatter.Format(addr)
// Output:
// <div class="postal-address" translate="no">
// <span class="postal-code">710043</span><br>
// <span class="region">陕西省</span><span class="locality">西安市</span><span class="sublocality">新城区</span><br>
// <span class="line1">幸福中路</span>
// </div>

Documentation

Overview

Package address handles address representation, validation and formatting.

Index

Examples

Constants

View Source
const CLDRVersion = "40.0.0"

CLDRVersion is the CLDR version from which the country list is derived.

Variables

This section is empty.

Functions

func CheckCountryCode

func CheckCountryCode(countryCode string) bool

CheckCountryCode checks whether the given country code is valid.

An empty country code is considered valid.

func GetCountryCodes

func GetCountryCodes() []string

GetCountryCodes returns all known country codes.

func GetCountryNames

func GetCountryNames() map[string]string

GetCountryNames returns all known country names, keyed by country code.

func GetFormats

func GetFormats() map[string]Format

GetFormats returns all known address formats, keyed by country code.

The ZZ address format represents the generic fallback.

Types

type Address

type Address struct {
	Line1 string `json:"line1"`
	Line2 string `json:"line2"`
	Line3 string `json:"line3"`
	// Sublocality is the neighborhood/suburb/district.
	Sublocality string `json:"sublocality"`
	// Locality is the city/village/post town.
	Locality string `json:"locality"`
	// Region is the state/province/prefecture.
	// An ISO code is used when available.
	Region string `json:"region"`
	// PostalCode is the postal/zip/pin code.
	PostalCode string `json:"postal_code"`
	// CountryCode is the two-letter code as defined by CLDR.
	CountryCode string `json:"country"`
}

Address represents an address.

func (Address) IsEmpty

func (a Address) IsEmpty() bool

IsEmpty returns whether a is empty.

type Field

type Field string

Field represents an address field.

const (
	FieldLine1       Field = "1"
	FieldLine2       Field = "2"
	FieldLine3       Field = "3"
	FieldSublocality Field = "S"
	FieldLocality    Field = "L"
	FieldRegion      Field = "R"
	FieldPostalCode  Field = "P"
)

type Format

type Format struct {
	Locale            Locale          `json:"locale,omitempty"`
	Layout            string          `json:"layout,omitempty"`
	LocalLayout       string          `json:"local_layout,omitempty"`
	Required          []Field         `json:"required,omitempty"`
	SublocalityType   SublocalityType `json:"sublocality_type,omitempty"`
	LocalityType      LocalityType    `json:"locality_type,omitempty"`
	RegionType        RegionType      `json:"region_type,omitempty"`
	PostalCodeType    PostalCodeType  `json:"postal_code_type,omitempty"`
	PostalCodePattern string          `json:"postal_code_pattern,omitempty"`
	ShowRegionID      bool            `json:"show_region_id,omitempty"`
	Regions           RegionMap       `json:"regions,omitempty"`
	LocalRegions      RegionMap       `json:"local_regions,omitempty"`
}

Format represents an address format.

func GetFormat

func GetFormat(countryCode string) Format

GetFormat returns an address format for the given country code.

func (Format) CheckPostalCode

func (f Format) CheckPostalCode(postalCode string) bool

CheckPostalCode checks whether the given postal code is valid.

An empty postal code is considered valid.

func (Format) CheckRegion

func (f Format) CheckRegion(region string) bool

CheckRegion checks whether the given region is valid.

An empty region is considered valid.

func (Format) CheckRequired

func (f Format) CheckRequired(field Field, value string) bool

CheckRequired checks whether a required field is valid (non-blank).

Non-required fields are considered valid even if they're blank.

func (Format) IsRequired

func (f Format) IsRequired(field Field) bool

IsRequired returns whether the given field is required.

func (Format) SelectLayout

func (f Format) SelectLayout(locale Locale) string

SelectLayout selects the correct layout for the given locale.

func (Format) SelectRegions

func (f Format) SelectRegions(locale Locale) RegionMap

SelectRegions selects the correct regions for the given locale.

type FormatHandler

type FormatHandler struct{}

FormatHandler is an HTTP handler for serving address formats.

Response size is ~45kb, or ~14kb if gzip compression is used.

The locale can be provided either as a query string (?locale=fr) or as a header (Accept-Language:fr). Defaults to "en".

func (*FormatHandler) ServeHTTP

func (h *FormatHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements the http.Handler interface.

type Formatter

type Formatter struct {

	// CountryMapper maps country codes to country names.
	// Can be used to retrieve country names from another (localized) source.
	// Defaults to a function that uses English country names included in the package.
	CountryMapper func(countryCode string, locale Locale) string
	// NoCountry turns off displaying the country name.
	// Defaults to false.
	NoCountry bool
	// WrapperElement is the wrapper HTML element.
	// Defaults to "p".
	WrapperElement string
	// WrapperClass is the wrapper HTML class.
	// Defaults to "address".
	WrapperClass string
	// contains filtered or unexported fields
}

Formatter formats addresses for display.

func NewFormatter

func NewFormatter(locale Locale) *Formatter

NewFormatter creates a new formatter for the given locale.

func (*Formatter) Format

func (f *Formatter) Format(addr Address) string

Format formats the given address.

Example
package main

import (
	"fmt"

	"github.com/bojanz/address"
)

func main() {
	locale := address.NewLocale("en")
	formatter := address.NewFormatter(locale)
	addr := address.Address{
		Line1:       "1098 Alta Ave",
		Locality:    "Mountain View",
		Region:      "CA",
		PostalCode:  "94043",
		CountryCode: "US",
	}
	fmt.Println(formatter.Format(addr))

	addr = address.Address{
		Line1:       "幸福中路",
		Sublocality: "新城区",
		Locality:    "西安市",
		Region:      "SN",
		PostalCode:  "710043",
		CountryCode: "CN",
	}
	locale = address.NewLocale("zh")
	formatter = address.NewFormatter(locale)
	formatter.NoCountry = true
	formatter.WrapperElement = "div"
	formatter.WrapperClass = "postal-address"
	fmt.Println(formatter.Format(addr))
}
Output:

<p class="address" translate="no">
<span class="line1">1098 Alta Ave</span><br>
<span class="locality">Mountain View</span>, <span class="region">CA</span> <span class="postal-code">94043</span><br>
<span class="country" data-value="US">United States</span>
</p>
<div class="postal-address" translate="no">
<span class="postal-code">710043</span><br>
<span class="region">陕西省</span><span class="locality">西安市</span><span class="sublocality">新城区</span><br>
<span class="line1">幸福中路</span>
</div>

func (*Formatter) Locale

func (f *Formatter) Locale() Locale

Locale returns the locale.

type Locale

type Locale struct {
	Language  string
	Script    string
	Territory string
}

Locale represents a Unicode locale identifier.

func NewLocale

func NewLocale(id string) Locale

NewLocale creates a new Locale from its string representation.

Example
package main

import (
	"fmt"

	"github.com/bojanz/address"
)

func main() {
	firstLocale := address.NewLocale("en-US")
	fmt.Println(firstLocale)
	fmt.Println(firstLocale.Language, firstLocale.Territory)

	// Locale IDs are normalized.
	secondLocale := address.NewLocale("sr_rs_latn")
	fmt.Println(secondLocale)
	fmt.Println(secondLocale.Language, secondLocale.Script, secondLocale.Territory)
}
Output:

en-US
en US
sr-Latn-RS
sr Latn RS

func (Locale) IsEmpty

func (l Locale) IsEmpty() bool

IsEmpty returns whether l is empty.

func (Locale) MarshalText

func (l Locale) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface.

func (Locale) String

func (l Locale) String() string

String returns the string representation of l.

func (*Locale) UnmarshalText

func (l *Locale) UnmarshalText(b []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface.

type LocalityType

type LocalityType uint8

LocalityType represents the locality type.

const (
	LocalityTypeCity LocalityType = iota
	LocalityTypeDistrict
	LocalityTypePostTown
	LocalityTypeSuburb
)

func (LocalityType) MarshalText

func (l LocalityType) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface.

func (LocalityType) String

func (l LocalityType) String() string

String returns the string representation of l.

func (*LocalityType) UnmarshalText

func (l *LocalityType) UnmarshalText(b []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface.

type PostalCodeType

type PostalCodeType uint8

PostalCodeType represents the postal code type.

const (
	PostalCodeTypePostal PostalCodeType = iota
	PostalCodeTypeEir
	PostalCodeTypePin
	PostalCodeTypeZip
)

func (PostalCodeType) MarshalText

func (p PostalCodeType) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface.

func (PostalCodeType) String

func (p PostalCodeType) String() string

String returns the string representation of p.

func (*PostalCodeType) UnmarshalText

func (p *PostalCodeType) UnmarshalText(b []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface.

type RegionMap

type RegionMap struct {
	// contains filtered or unexported fields
}

RegionMap represents a read-only ordered map of regions.

func NewRegionMap

func NewRegionMap(pairs ...string) RegionMap

NewRegionMap creates a new region map from the given pairs.

func (RegionMap) Get

func (r RegionMap) Get(key string) (string, bool)

Get returns the value for the given key, or an empty string if none found.

func (RegionMap) HasKey

func (r RegionMap) HasKey(key string) bool

HasKey returns whether the given key exists in the map.

func (RegionMap) Keys

func (r RegionMap) Keys() []string

Keys returns a list of keys.

func (RegionMap) Len

func (r RegionMap) Len() int

Len returns the number of keys in the map.

func (RegionMap) MarshalJSON

func (r RegionMap) MarshalJSON() ([]byte, error)

type RegionType

type RegionType uint8

RegionType represents the region type.

const (
	RegionTypeProvince RegionType = iota
	RegionTypeArea
	RegionTypeCanton
	RegionTypeCounty
	RegionTypeDepartment
	RegionTypeDistrict
	RegionTypeDoSi
	RegionTypeEmirate
	RegionTypeIsland
	RegionTypeOblast
	RegionTypeParish
	RegionTypePrefecture
	RegionTypeState
)

func (RegionType) MarshalText

func (r RegionType) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface.

func (RegionType) String

func (r RegionType) String() string

String returns the string representation of r.

func (*RegionType) UnmarshalText

func (r *RegionType) UnmarshalText(b []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface.

type SublocalityType

type SublocalityType uint8

SublocalityType represents the sublocality type.

const (
	SublocalityTypeSuburb SublocalityType = iota
	SublocalityTypeDistrict
	SublocalityTypeNeighborhood
	SublocalityTypeVillageTownship
	SublocalityTypeTownland
)

func (SublocalityType) MarshalText

func (s SublocalityType) MarshalText() ([]byte, error)

MarshalText implements the encoding.TextMarshaler interface.

func (SublocalityType) String

func (s SublocalityType) String() string

String returns the string representation of s.

func (*SublocalityType) UnmarshalText

func (s *SublocalityType) UnmarshalText(b []byte) error

UnmarshalText implements the encoding.TextUnmarshaler interface.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL