Etsy Icon>

Code as Craft

How Etsy Localizes Addresses main image

How Etsy Localizes Addresses


Imagine you’re browsing the web from your overpriced California apartment one day and you find a neat new website with some really cool stuff. You pick out a few items, add them to your cart, and start the checkout process. You get to the part where they ask for your shipping address and this is the form you see:  


It starts off easy - you fill in your name, your street, your apartment number, and then you reach the field labelled “Post Town”. What is a “Post Town”? Huh. Next you see “County”. Well, you know what a county is, but since when do you list it in your address? Then there’s “Postal code”. You might recognize it as what the US calls a “zip code”, but it’s still confusing to see. So now you don’t really know what to do, right? Where do you put your city, or your state? Do you just cram your address into the form however you can and hope that you get your order? Or do you abandon your cart and decide not to buy anything from this site?

This is in fact a fake form I put together for this exercise, but it demonstrates exactly how a lot of our members outside the United States felt when we showed them  a US-centric address form.

Etsy does business in more than 200 countries, and it’s important that our members feel comfortable and confident when providing their shipping address and placing orders. They need to know that their orders are going to reach them. Furthermore, we ask for several addresses when new sellers join Etsy, like billing and banking addresses, and we want them to feel comfortable providing this information, and confident that we will know how to support them and their needs.

In this post I’ll cover:

  • Where we started: Generic Address Forms
  • What are international addresses supposed to look like?
  • Building a Localized Address Experience

Where we started: Generic Address Forms

When we first started this project, our address forms were designed in a variety of technologies, with minimal localization. Most address forms worked well for US members, and we displayed the appropriate forms for Canadian and Australian members, but members in every other country were shown a generic, unlocalized address form.

United States


Our address form for US members looks just fine - all the fields we expect to see are there, and there aren’t any unexpected fields.

Germany (et al)


This is the form we showed for Germany (and most other countries). We asked for a postal code, and a state, province, or region. For someone unfamiliar with German addresses, this might seem fine at first. But what if I told you that German addresses don’t have states? Even worse, in this form, state is a required field!  

This form confused a lot of our German members, and they ended up putting any number of things in that field, just to be able to move forward. This led us to saved addresses like:

Ets Y. Crafter 123 Main Street Berlin, Berlin 12435 Germany

In this case, the member just entered the city in the state field. This wasn’t the worst situation, and anything shipped to this address would probably arrive just fine.

But what about this address?

Ets Y. Crafter 123 Main Street Berlin, California 12435 Germany

Sometimes the member entered a US state in the state field. This confused sellers and postal workers alike - we had real life examples of packages being shipped to the US because a state was included, even though the country listed was something totally different!

Ets Y. Crafter 123 Main Street Berlin, dasdsaklklg Germany

Members could even enter gibberish in the state field. Again, this was a bit confusing for sellers and the postal service.  

What are non-US addresses supposed to look like?

Here’s an example of a German address:

Ets Y. Crafter 123 Main Street 12435 BERLIN Germany

If you wanted to mail something to this address, you’d need to specify the recipient, the street name and number, the postal code and city, and the country. We could have used this address to determine an address format for Germany, but what about the almost 200 other countries Etsy supports? We didn’t really want look up sample addresses for each country and guess at what the address format should be.

Thankfully, we didn’t have to do that.

We drew on 3 different sources when compiling a list of address formats for different countries.

  • The majority of our data came from Google’s Address Data Service.
  • We also did a little sanity checking using the Universal Postal Union (or UPU) , an international organization based out of Switzerland that basically tries to make sure all the different postal carriers in the world can work together.
  • We also consulted our international staff to make sure the data for certain markets, like Germany and Japan, was correct.

So what kind of data did we get?

The most important piece of information we got is a format string that tells us:

  • Which fields are part of the address
  • The order they should be displayed in
  • Any delimiters (spaces, commas, newlines) between each field

We also got other formatting data, including:

  • The ISO standard country code
  • An array indicating which fields are required for a complete address
  • Administrative area (state) data
  • Regular expression (regex) patterns for postal code validation
  • Detail on what each field is called (e.g. State, Province, Region, or Prefecture)

Here’s what the formatting data looks like for a couple of different countries, and how that data is used to assemble the localized address form for that country.

United States

$format = [
  209 => [
    'format' => '%name\n%first_line\n%second_line\n%city, %state %zip\n%country_name',
    'required_fields' => [
      'state', 'zip',
    'uppercase_fields' => [
    'name' => 'UNITED STATES',
    'administrative_area_type' => 'state',
    'locality_type' => 'city',
    'postal_code_type' => 'zip',
    'postal_code_pattern' => '(\\d{5})(?:[ \\-](\\d{4}))?',
    'administrative_areas' => [
      'AL' => 'Alabama',
      'AK' => 'Alaska',
      'WI' => 'Wisconsin',
      'WY' => 'Wyoming',
    'iso_code' => 'US',



$format = [
  91 => [
    'format' => '%name\n%first_line\n%second_line\n%zip%city\n%country_name',
    'required_fields' => [
    'uppercase_fields' => [
    'name' => 'GERMANY',
    'locality_type' => 'city',
    'postal_code_type' => 'postal',
    'postal_code_pattern' => '\\d{5}',
    'input_format' => '%name\\n%first_line\\n%second_line\\n%zip\\n%city\\n%country_name',
    'iso_code' => 'DE',

So, now we had all this great information on what addresses were supposed to look like for almost 200 countries. How did we take this data and turn it into a localized address experience?  

Building a Localized Address Experience

A complete localized address experience requires two components: address input and address display. In other words, our members need to be able to add and edit their addresses using a form that makes sense to them, and they need to see their address displayed in a format that they’re familiar with.

Address Input

You’ve already seen what our unlocalized address form looked like, but here’s a quick reminder of what German members were seeing when they were entering their addresses.


This was a static form, meaning we had a big template with a bunch of tags, and a little bit of JavaScript to handle interactions with the form. For a few select countries, like Canada and Australia, we added conditional statements to the template, swapping in different state or province lists as necessary. It made for a pretty messy template.

When deciding how we wanted to handle address forms, we knew that we didn’t want to have a template with enough conditional statements to handle hundreds of different address formats. Instead, we decided on a compositional approach.

Every address form starts with a country input. This prompts the member to select their country first, so we can render the localized form before they start entering their address. We identified all the possible fields that could be in an address form: first_line, second_line, city, state, zip, and country, and recognized that all these fields could be rendered using just a few generic templates. These templates would allow us to specify custom labels, indicate whether or not the field was required, display validation errors, and render other custom content by providing different data when we render the template for each field.   

Text Input

A pretty basic text input can be used for the first line, second line, city, and zip address fields, as well as the state field, depending on the country. Here’s what our text input template looks like:


State Select Input

For the countries for which we have state (aka administrative area) data, we created a select input template:


With these templates, and the appropriate address formatting data, we can generate address input forms for almost 200 countries.  

Address Display

Displaying localized addresses was also handled by a static template before this project. It was based on the US address format, and was written with the assumption that all addresses had the same fields as US addresses. It looked something like this:

  < p>{{name}}< /p>
  < p>{{first_line}}< /p>
  < p>{{second_line}}< /p>
  < p>{{city}},{{state}}{{zip}}< /p>
  < p>{{country_name}}< /p>

While this wasn’t as problematic as the way we were handling address input, it was still not ideal. Addresses for international members would be displayed incorrectly, causing varying levels of confusion. For German members, the difference wasn’t too bad:

But for members in countries like Japan, the difference was pretty significant:

To localize address display, we went with a compositional approach again, treating each field as a separate piece, and then combining them in the order specified, and using the delimiters indicated by the format string.

  < span class="name">EtsY.Crafter< /span>< br>
  < span class="first-line">123MainStreet< /span>< br/>
  < span class="zip">12345< /span>< span class="city">BERLIN< /span>< br/>
  < span class="country-name">Germany< /span>< br/>

We further enhanced our address display library by creating a PHP class that could render a localized address in plaintext, or fully customizable HTML, to support the numerous ways addresses are displayed throughout Etsy and our internal tools.


No more confusing address forms! While we’re nowhere near finished with localized addresses, we’ve made really great progress so far. We’re hopeful that our members will enjoy their experience just a little bit more now that they have fewer concerns when it comes to addresses. There is a lot more that we learned from this project (like how we replaced the unlocalized address forms with the localized address form on the entire site!), so keep an eye out for future blog posts. Thanks for reading!