Robin van der Vleuten

Handle international addresses in Ruby

You're building an e-commerce site, and a customer from Japan reaches checkout. Your form asks for a "State" and a "ZIP code."

Japan does not use that shape.

Or maybe you're printing shipping labels and discover that the USPS wants certain fields in ALL CAPS for automated sorting, but you have no idea which fields or why.

International address handling gets messy fast.

The problem nobody warns you about

A checkout form has to deal with details like these:

  • The UK calls it a "postcode." Americans call it a "ZIP code." Canadians call it a "postal code."
  • Japanese addresses go from largest to smallest (the complete opposite of Western addresses)
  • Some countries don't have postal codes at all
  • Ireland uses "County" but the US uses "State" and Canada uses "Province" (or sometimes "Territory")
  • For automated mail sorting, you need to uppercase specific fields in specific countries

You can spend weeks researching postal systems across hundreds of countries. I would rather use a library that already packages that data.

That is why I built Addressing.

What it does

Addressing is a Ruby gem for postal address details across countries:

  • 250+ country definitions with translations powered by CLDR data
  • 200+ address formats, so you know which fields to show and in what order
  • subdivisions for 60 countries: states, provinces, prefectures, and whatever they call them locally
  • formatting in HTML and plain text
  • Active Record validation

Your first address

A simple address looks like this:

ruby
address = Addressing::Address.new(
country_code: "US",
administrative_area: "CA",
locality: "Mountain View",
address_line1: "1600 Amphitheatre Parkway",
given_name: "Sundar",
family_name: "Pichai"
)
formatter = Addressing::DefaultFormatter.new
puts formatter.format(address)

That outputs HTML with the fields in the right order for the US, plus semantic CSS classes. The country name is localized automatically.

You do not have to guess the field order or hardcode it into the form.

Real-world examples

Building a checkout form that works everywhere

Different countries need different fields. Nobody in Japan needs a "State" dropdown, but they absolutely need a "Prefecture" field:

ruby
# Get the address format for Japan
format = Addressing::AddressFormat.get('JP')
# Build your form dynamically
format.used_fields.each do |field|
puts "#{format.label_for(field)}: [input field]"
end

Your Japanese customers see the fields they expect. Your UK customers don't see irrelevant fields. That is the important part: the form matches the country.

Rails validation

Your model needs to accept addresses from anywhere:

ruby
class Order < ApplicationRecord
validates_address_format
end

Postal codes get validated against country-specific patterns. Required fields get enforced. Customers can complete checkout without fighting a form built for another country.

Printing shipping labels

You're printing labels for international shipments. Some countries require specific fields in ALL CAPS for automated sorting:

ruby
address = Addressing::Address.new(
country_code: "US",
administrative_area: "CA",
locality: "Mountain View",
address_line1: "1098 Alta Ave"
)
formatter = Addressing::PostalLabelFormatter.new(origin_country: "FR")
puts formatter.format(address)
# Output:
# 1098 Alta Ave
# MOUNTAIN VIEW, CA 94043
# ÉTATS-UNIS - UNITED STATES

Notice what happened:

  • City and state got uppercased because USPS expects that.
  • The country name appears in both French and English, following the Universal Postal Union recommendation.
  • The label is formatted for international mail from France.

The formatter handles the postal label rules for you.

Country selectors in any language

Need a dropdown in the user's language?

ruby
# Get all countries in Japanese
countries = Addressing::Country.list('ja-JP')
# => { "JP" => "日本", "US" => "アメリカ合衆国", ... }
# Or get detailed info
brazil = Addressing::Country.get('BR')
puts brazil.currency_code # "BRL"
puts brazil.timezones # All timezones Brazil spans

French users see "États-Unis." Japanese users see "アメリカ合衆国." The labels come from the locale data.

Cascading location dropdowns

For forms where selecting a country shows states, then cities:

ruby
# Get all states in Brazil
states = Addressing::Subdivision.all(['BR'])
# User selected Ceará? Get municipalities
municipalities = Addressing::Subdivision.all(['BR', 'CE'])
# Traverse the hierarchy
states.each do |state|
state.children.each do |municipality|
puts "#{state.name} - #{municipality.name}"
end
end

The data is hierarchical and translated, so the form can follow the selected country.

Why I would not roll this by hand

The gem builds on Google's Address Data Service and the CLDR project. One bundle install gives you the country data, labels, formats, and subdivisions without turning your checkout into a postal research project.

It also keeps the odd cases close to the code. Singapore uses six-digit postal codes. Russia uses Cyrillic addresses. Some places have no postal codes at all. Those are not details I want scattered through view helpers.

Address formats change over time too. Countries split, subdivisions get renamed, and postal rules move. Tracking CLDR updates in one library is much easier than rediscovering those changes in every app.

This Ruby version ports the PHP addressing library from CommerceGuys, which has already done this work for major e-commerce platforms.

When to use this

Use it when addresses are more than a text box:

  • checkout flows where wrong addresses mean failed deliveries
  • booking systems that collect international customer details
  • multi-tenant SaaS where customers operate in different countries
  • shipping tools that need postal labels to follow local rules

Getting started

Check out Addressing on GitHub for installation and complete documentation.

Add it to your Gemfile, then wire up the validators and formatters where addresses enter or leave your application.

Wrong addresses cost real money through failed shipments and frustrated customers. This gem turns a lot of postal research into an API you can call directly.

Next time you are about to add a "State" field to an international form, check whether the selected country even has one.