Phone Number Formatter

Writes a phone number as E.164 to store and nationally to display, in twenty countries, and says why a length check is not validation.

Enable JavaScript to customise; default output below.

One a line, or comma separated. Brackets, dashes, spaces and a trailing extension are all understood.

The country the number belongs to, which is how a leading zero or a leading 1 is read.

Output

E.164 alone gives a list you can paste into a database column.

Live preview phone-numbers.txt
Country                               United Kingdom, +44
Numbers in                            3
Parsed                                3

020 7946 0958
  E.164, to store                     +442079460958
  national, to show                   020 7946 0958
  international                       +44 20 7946 0958
  in a link                           tel:+442079460958
  dropped                             the trunk prefix 0

07700 900123
  E.164, to store                     +447700900123
  national, to show                   07700 900123
  international                       +44 7700 900123
  in a link                           tel:+447700900123
  dropped                             the trunk prefix 0

0121 496 0000 x24
  E.164, to store                     +441214960000
  national, to show                   0121 496 0000
  international                       +44 121 496 0000
  in a link                           tel:+441214960000;ext=24
  extension                           24, which no carrier dials for you
  dropped                             the trunk prefix 0

How United Kingdom numbers are built
  country code                        +44
  trunk prefix                        0, dialled at home and dropped in E.164
  digits after it                     9 or 10
  structure                           area codes of two, three or four digits, plus the 7 range for mobiles

Store E.164 and nothing else: a plus, the country code, the number, no
spaces. It is unambiguous, it is what every gateway and CRM accepts, and
it sorts and compares correctly. Store the national format and you have
thrown away the country.

Display the national format. `020 7946 0958` is how a British reader
recognises a number and `+442079460958` is not, so the two exist for
different jobs and the conversion is cheap in both directions.

The leading 0 is a dialling instruction rather than part of the number,
which is why E.164 has no room for it. Italy is the country where that
is not true, and it catches people out.

A length check is not validation. A number of the right length can be
unassigned, disconnected, a landline that cannot receive a text, or a
VoIP number in a different country to the one it looks like. The only
real check is sending a code to it.

In HTML use `tel:` with the E.164 value, not the pretty one: `<a
href="tel:+442079460958">020 7946 0958</a>`. Punctuation in the href is
tolerated by most dialers and not by all of them, and a leading zero in
an international context is dialled as a zero.

An extension is not part of the number. It belongs in its own field, the
`;ext=` parameter exists for a link, and no carrier dials it for the
caller: an IVR has to hear it as tones after the call connects.

Store what the person typed as well. If the parse turns out to be wrong,
the original is the only way to recover, and a form that silently
rewrites a number to something undeliverable is a support ticket nobody
can trace.

The table here covers the common ranges in each country. The complete
one is libphonenumber, which is about a megabyte and is updated when a
regulator opens a new range. Anything charging money per message should
use it rather than a length check.

Output is valid and updates as you type.

There are two phone number formats and they do different jobs. E.164 is for storing: a plus, the country code, the number, no punctuation. The national format is for reading: 020 7946 0958.

Storing the national one throws away the country. Displaying the E.164 one makes the number unrecognisable to the person it belongs to. So the answer is to store E.164, keep what the person typed alongside it, and format for display.

The interesting part is the leading zero. In most countries it is a trunk prefix, a domestic dialling instruction, and it does not belong in the number: 020 7946 0958 is +442079460958 with no zero anywhere. In Italy the zero is part of the number and is kept. In North America the trunk prefix is 1, which is also the country code, which is why 1 555 010 0199 and +1 555 010 0199 are the same number.

How to use

  1. Paste a number, or a list one per line.
  2. Pick the country the number belongs to. That is what tells the parser how to read a leading zero or a leading 1.
  3. Copy the E.164 column into your database and the national column into your page.

Example

Three British numbers, one with an extension:

Country                     United Kingdom, +44
Numbers in                  3
Parsed                      3

020 7946 0958
  E.164, to store           +442079460958
  national, to show         020 7946 0958
  international             +44 20 7946 0958
  in a link                 tel:+442079460958
  dropped                   the trunk prefix 0

0121 496 0000 x24
  E.164, to store           +441214960000
  national, to show         0121 496 0000
  international             +44 121 496 0000
  in a link                 tel:+441214960000;ext=24
  extension                 24, which no carrier dials for you
  dropped                   the trunk prefix 0

The extension is not part of the number. It has its own parameter in a tel: link, and no carrier dials it for the caller: an IVR has to hear it as tones after the call connects.

Pitfalls

A length check is not validation. A number of exactly the right length can be unassigned, disconnected, a landline that cannot receive a text, or a VoIP number registered in a different country to the one its code suggests. This tool will happily format a nine-digit British number that does not exist. The only real check is sending a code to it.

Italy keeps its leading zero. Rome is +39 06 6982, with the zero, in E.164. Strip it the way the rest of Europe needs and the number stops working. It is the exception worth remembering because every generic “remove the leading zero” rule breaks it.

The North American 1 is both a trunk prefix and a country code. Eleven digits starting with 1 and ten digits are the same number. No area code in the plan starts with 1, which is what makes stripping it unambiguous.

Do not store the formatted number. Formatting is presentation and it changes: Mexico dropped its trunk prefixes in 2019 and Brazil added a ninth digit to mobiles. E.164 does not change, and reformatting from it is cheap.

Use E.164 in a tel: link. <a href="tel:+442079460958">020 7946 0958</a>. Punctuation in the href is tolerated by most dialers and not by all, and a leading zero without a country code is dialled as a zero from abroad.

Never reject a number for looking unusual. Area codes vary from one digit to five, subscriber numbers from six to eleven, and regulators open new ranges regularly. A form that refuses a valid number loses the customer and never hears why.

Comparing numbers means comparing E.164. Two rows holding 020 7946 0958 and +44 20 7946 0958 are the same customer and no query will tell you that.

Compatibility

Everything runs in the browser: nothing is uploaded and nothing is stored, which matters because a list of phone numbers is personal data under most privacy law.

Twenty countries are covered, each with its country code, trunk prefix, the lengths it uses and the grouping that country writes. Every case in the test suite is a number written the way a person in that country writes it, checked against the E.164 a gateway expects: British mobiles and the two-, three- and four-digit area codes, German mobiles against Berlin, Tokyo’s one-digit area code, Brazil’s ninth digit, Italy’s kept zero, and the North American 1 in all three of its positions.

The complete data set is Google’s libphonenumber. It is about a megabyte, it is updated whenever a regulator opens a range, and it is the right choice for anything that charges money per message. The table here covers the common ranges and says which ones, rather than implying it covers everything.

Extensions are recognised after x, ext, ext., extension or #, and are reported separately rather than being glued onto the number.

Frequently asked questions

Which field should I store in the database?
One column for E.164 and one for the raw input. E.164 is what you send, compare and index; the raw input is the only way to recover if the parse was wrong. A separate country column is worth having if you ever need to format for display without re-parsing.
Why does the parser want to know the country?
Because a leading zero is meaningless without it. 06 12345678 is a Dutch mobile, an Italian Rome landline and a French number depending on where it was written, and the three give different E.164 values.
What about a number with no country code and no zero?
It is ambiguous, and the country you pick decides it. For a form, either ask for the country or require the plus: guessing from the visitor’s IP address gets it wrong for travellers and for anyone using a VPN.
Can I detect a mobile from the number?
Sometimes, from the prefix: British mobiles start 07, Dutch 06, Australian 04. It is a convention rather than a rule, portability moves numbers between carriers, and in North America mobile and landline share the same ranges entirely.
How do I validate a number properly?
Format it, then send a code to it. Everything before that step is a filter for obvious typos, which is worth doing, and is not the same thing as knowing the number works.
Weekly drops

New tools, when there are new tools

One email when something worth using ships. No schedule to fill, so no filler.

Your address goes nowhere else, and one click unsubscribes.