Home

Awesome

libphonenumber-js

npm version npm downloads coverage

A simpler and smaller rewrite of Google Android's libphonenumber library in javascript.

See Demo

If you’re trying to build a React component with it, take a look at react-phone-number-input.

LibPhoneNumber

Google's libphonenumber is an ultimate phone number formatting and parsing library developed by Google for Android phones. It is written in C++ and Java, and, while it has an official autogenerated javascript port, that port is tightly coupled to Google's closure javascript framework, and, when compiled into a bundle, weighs about 550 kB (350 kB code + 200 kB metadata).

With many websites today asking for user's phone number, the internet could benefit from a simpler and smaller library that would just get the parsing and formatting right, and that's what libphonenumber-js is.

<!-- That's what `libphonenumber-js` is about: I started off from scratch, decyphering Google's code and figuring out how this whole machinery works, through trial and error, with several years of hacking around, receiving bug reports from other developers — eventually, it has reached a state when it can be assumed production-ready. --> <!-- One part of me was curious about how all this phone number parsing and formatting machinery worked, and another part of me was curious if there was a way to reduce those 530 kilobytes to something more reasonable while also getting rid of all the unnecessary bulk and rewriting it all in pure javascript. The resulting library does everything a modern web application needs while maintaining a much smaller size of about 130 kilobytes. -->

Difference from Google's libphonenumber

<!-- (Australia, Bolivia, Brazil, China, Colombia, Croatia, Faroe Islands, South Korea, Liechtenstein, Luxembourg, Venezuela) --> <!-- for (var code of Object.keys(metadata.countries)) { var country = metadata.countries[code] if (country.formats && country.formats.find(_ => _.domestic_carrier_code_formatting_rule)) { console.log(code) } } --> <!-- * Doesn't use ["carrier codes"](https://github.com/googlei18n/libphonenumber/blob/master/FALSEHOODS.md) when formatting numbers: "carrier codes" are only used in Colombia and Brazil and only when dialing within those countries from a mobile phone to a fixed line number. (`.formatNumberForMobileDialing()` method is not implemented therefore there's no need to format carrier codes) -->

GitHub

On March 9th, 2020, GitHub, Inc. silently banned my account (erasing all my repos, issues and comments, even in my employer's private repos) without any notice or explanation. Because of that, all source codes had to be promptly moved to GitLab. The GitHub repo is now only used as a backup (you can star the repo there too), and the primary repo is now the GitLab one. Issues can be reported in any repo.

Install

via npm

$ npm install libphonenumber-js --save

via yarn

$ yarn add libphonenumber-js

If you're not using a bundler then use a standalone version from a CDN.

Use

<!-- ### Importing When using [Babel](https://babeljs.io/) or [`esm`](npmjs.com/package/esm) or [rollup.js](https://rollupjs.org/) or any other `import` transpiler: ```js import parsePhoneNumber, { isPossiblePhoneNumber, isValidPhoneNumber, AsYouType, findPhoneNumbersInText } from 'libphonenumber-js' ``` When using Node.js 14 and not using any of the above: ```js import parsePhoneNumber from 'libphonenumber-js' // or import libphonenumber from 'libphonenumber-js' const { default: parsePhoneNumber, isPossiblePhoneNumber, isValidPhoneNumber, AsYouType, findPhoneNumbersInText } = libphonenumber ``` When using Node.js < 14 and not using any of the above: ```js const libphonenumber = require('libphonenumber-js') const { default: parsePhoneNumber, isPossiblePhoneNumber, isValidPhoneNumber, AsYouType, findPhoneNumbersInText } = libphonenumber ``` https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/42 -->

Parse phone number

<!-- _(new API)_ -->
import parsePhoneNumber from 'libphonenumber-js'

const phoneNumber = parsePhoneNumber(' 8 (800) 555-35-35 ', 'RU')
if (phoneNumber) {
  phoneNumber.country === 'RU'
  phoneNumber.number === '+78005553535'
  phoneNumber.isPossible() === true
  phoneNumber.isValid() === true
  // Note: `.getType()` requires `/max` metadata: see below for an explanation.
  phoneNumber.getType() === 'TOLL_FREE'
}
<!-- <details> <summary>Legacy API</summary> ```js import { parseNumber } from 'libphonenumber-js' parseNumber('Phone: 8 (800) 555 35 35.', 'RU') // Outputs: { country: 'RU', phone: '8005553535' } ``` </details> -->

Validate phone number

import {
  isPossiblePhoneNumber,
  isValidPhoneNumber,
  validatePhoneNumberLength
} from 'libphonenumber-js'

isPossiblePhoneNumber('8 (800) 555-35-35', 'RU') === true
isValidPhoneNumber('8 (800) 555-35-35', 'RU') === true

validatePhoneNumberLength('8 (800) 555', 'RU') === 'TOO_SHORT'
validatePhoneNumberLength('8 (800) 555-35-35', 'RU') === undefined // Length is valid.

isPossiblePhoneNumber() only validates phone number length, while isValidPhoneNumber() validates both phone number length and the actual phone number digits.

validatePhoneNumberLength() is just a more detailed version of isPossiblePhoneNumber() — if the phone number length is invalid, it returns the actual reason: TOO_SHORT, TOO_LONG, etc.

Format phone number

<!-- _(new API)_ -->
import parsePhoneNumber from 'libphonenumber-js'

const phoneNumber = parsePhoneNumber('+12133734253')

phoneNumber.formatInternational() === '+1 213 373 4253'
phoneNumber.formatNational() === '(213) 373-4253'
phoneNumber.getURI() === 'tel:+12133734253'
<!-- <details> <summary>Legacy API</summary> ```js import { formatNumber } from 'libphonenumber-js' formatNumber('+12133734253', 'INTERNATIONAL') // Outputs: '+1 213 373 4253' formatNumber('+12133734253', 'NATIONAL') // Outputs: '(213) 373-4253' formatNumber({ country: 'US', phone: '2133734253' }, 'INTERNATIONAL') // Outputs: '+1 213 373 4253' formatNumber({ country: 'US', phone: '2133734253' }, 'NATIONAL') // Outputs: '(213) 373-4253' ``` </details> -->

"As You Type" formatter

import { AsYouType } from 'libphonenumber-js'

new AsYouType().input('+12133734')
// Outputs: '+1 213 373 4'

new AsYouType('US').input('2133734')
// Outputs: '(213) 373-4'

Full-text search

<!-- _(new API)_ -->
import { findPhoneNumbersInText } from 'libphonenumber-js'

findPhoneNumbersInText(`
  For tech support call +7 (800) 555-35-35 internationally
  or reach a local US branch at (213) 373-4253 ext. 1234.
`, 'US')

// Outputs:
//
// [{
//   number: PhoneNumber {
//     country: 'RU',
//     countryCallingCode: '7',
//     number: '+78005553535',
//     nationalNumber: '8005553535'
//   },
//   startsAt : 22,
//   endsAt   : 40
// }, {
//   number: PhoneNumber {
//     country: 'US',
//     countryCallingCode: '1',
//     number: '+12133734253',
//     nationalNumber: '2133734253',
//     ext: '1234'
//   },
//   startsAt : 86,
//   endsAt   : 110
// }]

"min" vs "max" vs "mobile" vs "core"

This library provides different "metadata" sets, "metadata" being a list of phone number parsing and formatting rules for all countries. The complete list of those rules is huge, so this library provides a way to optimize bundle size by choosing between max, min, mobile and "custom" metadata:

To use a particular metadata set, simply import functions from a relevant sub-package:

Importing functions directly from libphonenumber-js effectively results in using the min metadata.

Sometimes (rarely) not all countries are needed, and in those cases developers may want to generate their own "custom" metadata set. For those cases, there's libphonenumber-js/core sub-package which doesn't come pre-packaged with any default metadata set and instead accepts metadata as the last argument of each exported function.

Definitions

Country code

A "country code" is a two-letter ISO country code (like US). <!-- or a special `001` country code used for ["non-geographic entities"](#non-geographic) (as per [Google's libphonenumber library](https://github.com/googlei18n/libphonenumber/blob/0068d861a68d3d4612f7bf8646ab844dd3cefce5/java/libphonenumber/test/com/google/i18n/phonenumbers/RegionCode.java#L23-L24)). -->

This library supports all officially assigned ISO alpha-2 country codes, plus a few extra ones like: AC (Ascension Island), TA (Tristan da Cunha), XK (Kosovo).

<!-- var countries = [] for (const code of Object.keys(country_calling_codes)) { countries = countries.concat(country_calling_codes[code]) } console.log(countries.length) -->

To check whether a country code is supported, use isSupportedCountry() function.

Non-geographic

There're several calling codes that don't belong to any country:

Such phone numbering plans are called "non-geographic", and their phone numbers have country set to undefined.

National (significant) number

"National (significant) number" are the national phone number digits (without "national prefix"). For example, +1 213 373 4253 (or (213) 373-4253 in national format) is a US phone number and its national (significant) number is 213 373 4253. Another example is +33 1 45 45 32 45 (or 01 45 45 32 45 in national format) which is a French phone number where they add 0 "national prefix" when writing phone numbers in national format; in this case the national (significant) number is 1 45 45 32 45.

Country calling code

"Country calling code" are the digits between the + and the national (significant) number when the number is written in international format. E.g. for US country calling code is 1 and for France it's 33. Several countries can share the same "country calling code", e.g. NANPA countries like USA and Canada sharing the same 1 country calling code.

API

parsePhoneNumber(string, defaultCountry?: string | options?: object): PhoneNumber

Parses a phone number from string.

Can be imported both as a "default" export and as a "named" export parsePhoneNumberFromString.

import parsePhoneNumber from 'libphonenumber-js'
// Or: import { parsePhoneNumberFromString as parsePhoneNumber } from 'libphonenumber-js'

const phoneNumber = parsePhoneNumber('(213) 373-42-53 ext. 1234', 'US')
if (phoneNumber) {
  console.log(phoneNumber.formatNational())
}

Returns an instance of PhoneNumber class, or undefined if no phone number could be parsed: for example, when the string contains no phone number, or the phone number starts with a non-existent country calling code, etc.

Available options:

If a developer wants to know the exact reason why the phone number couldn't be parsed then they can use parsePhoneNumberWithError() function which throws the exact error:

import { parsePhoneNumberWithError, ParseError } from 'libphonenumber-js'

try {
  const phoneNumber = parsePhoneNumberWithError('(213) 373-42-53 ext. 1234', {
    defaultCountry: 'US'
  })
} catch (error) {
  if (error instanceof ParseError) {
    // Not a phone number, non-existent country, etc.
    console.log(error.message)
  } else {
    throw error
  }
}
<details> <summary>Possible errors</summary> </details>

Strictness

By default, the parsing function will attempt to extract a phone number from an input string even in cases like "Support: (213) 373-4253 (robot)", which mimicks the behavior of the original Google's libphonenumber library, and is the default behavior for legacy reasons. However, if "strict" input validation is required, one can pass extract: false flag to demand that the whole input string be a viable phone number.

parsePhoneNumber('Call: (213) 373-4253', 'US') === PhoneNumber

// When parsing the same string with `extract: false` flag,
// it will return `undefined`, because a phone number can't
// contain letters or a colon.
parsePhoneNumber('Call: (213) 373-4253', {
  defaultCountry: 'US',
  extract: false
}) === undefined

parsePhoneNumber('(213) 373-4253', {
  defaultCountry: 'US',
  extract: false
}) === PhoneNumber

PhoneNumber

<!-- `PhoneNumber` class constructor accepts two arguments: `country`/`countryCallingCode` and `nationalNumber`. Also `metadata`. ```js const phoneNumber = new PhoneNumber('RU', '8005553535', metadata) ``` -->

PhoneNumber class instance has the following properties:

<!-- * `countryCallingCodeSource: string` — How the [country calling code](#country-calling-code) was obtained for this phone number. --> <!-- * `FROM_NUMBER_WITH_PLUS_SIGN` — The country_code is derived based on a phone number with a leading "+", e.g. the French number "+33 1 42 68 53 00". --> <!-- * `FROM_NUMBER_WITHOUT_PLUS_SIGN` — The country_code is derived based on a phone number without a leading "+", e.g. the French number "33 1 42 68 53 00" when defaultCountry is supplied as France. --> <!-- * `FROM_DEFAULT_COUNTRY` — The country_code is derived NOT based on the phone number itself, but from the defaultCountry parameter provided in the parsing function by the clients. This happens mostly for numbers written in the national format (without country code). For example, this would be set when parsing the French number "01 42 68 53 00", when defaultCountry is supplied as France. -->

PhoneNumber class instance provides the following methods:

setExt(ext: string)

Sets a phone number extension of a phone number. Could be useful when formatting phone numbers stored as two separate fields: the phone number itself and the extension part.

const phone = "+12133734253"
const phoneExt = "1234"

const phoneNumber = parsePhoneNumber(phone)
if (phoneNumber) {
  if (phoneExt) {
    phoneNumber.setExt(phoneExt)
  }
  // Returns "(213) 373-4253 ext. 1234"
  return phoneNumber.formatNational()
}

format(format: string, [options]): string

Formats the phone number into a string according to a format.

Available formats:

Available options:

Examples:

import parsePhoneNumber from 'libphonenumber-js'

const phoneNumber = parsePhoneNumber('+12133734253')

phoneNumber.format("NATIONAL") === '(213) 373-4253'
phoneNumber.format("INTERNATIONAL") === '+1 213 373 4253'
phoneNumber.format("RFC3966") === 'tel:+12133734253'

// Aliases
phoneNumber.formatNational() === phoneNumber.format("NATIONAL")
phoneNumber.formatInternational() === phoneNumber.format("INTERNATIONAL")
phoneNumber.getURI() === phoneNumber.format("RFC3966")

isPossible(): boolean

Checks if the phone number is "possible". Only checks the phone number length, doesn't check the actual phone number digits against any regular expressions.

isValid(): boolean

Checks if the phone number is "valid". First checks the phone number length and then checks the phone number digits against all available regular expressions.

By default the library uses "minimal" metadata which is only 75 kilobytes in size but also doesn't include the precise validation regular expressions resulting in less strict validation rules (some very basic validation like length check is still included for each country). If you don't mind the extra 65 kilobytes of metadata then use "full" metadata instead (140 kilobytes). Google's library always uses "full" metadata so it will yield different isValidNumber() results compared to the "minimal" metadata used by default in this library.

<details> <summary>See an example illustrating different results when using <code>/min</code> vs <code>/max</code> vs <code>/mobile</code></summary>

import parseMin from 'libphonenumber-js/min'
import parseMax from 'libphonenumber-js/max'
import parseMobile from 'libphonenumber-js/mobile'

// Mobile numbers in Singapore starting from `8`
// can only have the second digit in the range of `0..8`.
// Here the second digit is `9` which makes it an invalid mobile number.
// This is a "strict" (advanced) validation rule and is
// not included in the (default) "min" bundle.

// The basic number length check passes (`8..11`) and the
// "loose" national number validation regexp check passes too:
// `(?:1\d{3}|[369]|7000|8(?:\d{2})?)\d{7}`.
parseMin('+6589555555').isValid() === true

// The "advanced" validation regexp for mobile numbers is
// `(?:8[1-8]|9[0-8])\\d{6}` and possible lengths are `8`.
parseMax('+6589555555').isValid() === false
parseMobile('+6589555555').isValid() === false
</details>

See "Using phone number validation feature" for choosing between isPossible() and isValid().

<!-- #### `isValidForRegion(country)` Is just an alias for `this.isValid() && this.country === country`. https://github.com/googlei18n/libphonenumber/blob/master/FAQ.md#when-should-i-use-isvalidnumberforregion --> <!-- See the comments for `validateLength()` method in `PhoneNumber.js` class for the rationale on why is this method not part of the public API. #### `validateLength(): string?` Checks if the phone number length is valid. If it is, then nothing is returned. Otherwise, a rejection reason is returned. ```js parsePhoneNumber('444 1 44', 'TR').validateLength() === 'TOO_SHORT' parsePhoneNumber('444 1 444', 'TR').validateLength() === undefined // Length is valid. parsePhoneNumber('444 1 4444', 'TR').validateLength() === 'INVALID_LENGTH' parsePhoneNumber('444 1 44444', 'TR').validateLength() === 'INVALID_LENGTH' parsePhoneNumber('444 1 444444', 'TR').validateLength() === undefined // Length is valid. parsePhoneNumber('444 1 4444444444', 'TR').validateLength() === 'TOO_LONG' ``` <details> <summary>Possible rejection reasons</summary> * `NOT_A_NUMBER` — When the supplied string is not a phone number. For example, when there are no digits: `"abcde"`, `"+"`. * `INVALID_COUNTRY` * When `defaultCountry` doesn't exist (or isn't supported by this library yet): `parsePhoneNumber('(111) 222-3333', 'XX')`. * When parsing a non-international number without a `defaultCountry`: `parsePhoneNumber('(111) 222-3333')`. * When an international number's country calling code doesn't exist: `parsePhoneNumber('+9991112223333')`. * `TOO_SHORT` — When the number is too short. For example, just 1 or 2 digits: `"1"`, `"+12"`. * `TOO_LONG` — When the national (significant) number is too long (17 digits max) or when the string being parsed is too long (250 characters max). * `INVALID_LENGTH` — When the national (significant) number is neither too short, nor too long, but somewhere in between and its length is still invalid. </details> ###### `validateLength()` is just a more detailed version of `isPossible()` — if the phone number length is invalid, it returns the actual reason: `TOO_SHORT`, `TOO_LONG`, etc. -->

getPossibleCountries(): string[]

Returns a list of countries this phone number could possibly belong to.

Can be used when parsing complete international phone numbers containing a "calling code" that is shared between several countries. If parsing such a phone number returns country: undefined then getPossibleCountries() function could be used to somehow speculate about what country could this phone number possibly belong to.

getType(): string?

Returns phone number type (fixed line, mobile, toll free, etc) or undefined (if the number is invalid or if there are no phone number type regular expressions for this country in metadata).

By default the library uses "minimal" metadata which is only 75 kilobytes in size but also doesn't include the regular expressions for determining a specific phone number type (fixed line, mobile, toll free, etc) resulting in getType() returning undefined for most countries. If you don't mind the extra 65 kilobytes of metadata then use "full" metadata instead (140 kilobytes). Google's library always uses "full" metadata so it will yield different getNumberType() results compared to the "minimal" metadata used by default in this library.

<details> <summary>The list of possible return values</summary>

</details>

<details> <summary>See an example illustrating different results when using <code>/min</code> vs <code>/max</code> vs <code>/mobile</code></summary>

import parseMin from 'libphonenumber-js/min'
import parseMax from 'libphonenumber-js/max'
import parseMobile from 'libphonenumber-js/mobile'

// Singapore valid mobile number.

// The (default) "min" bundle doesn't contain any regexps for
// getting phone number type based on national number (for Singapore).
parseMin('+6584655555').getType() === undefined

// The "max" bundle contains regexps for
// getting phone number type based on national number
// for all possible phone number types.
parseMax('+6584655555').getType() === 'MOBILE'

// The "mobile" bundle contains regexps for
// getting phone number type based on national number
// for mobile phone numbers only.
parseMobile('+6584655555').getType() === 'MOBILE'
</details>

isNonGeographic(): boolean

Returns true if the number belongs to a "non-geographic numbering plan".

isEqual(phoneNumber: PhoneNumber): boolean

Compares two PhoneNumbers: returns true if they're equal, false otherwise.

isPossiblePhoneNumber(input: string, defaultCountry?: string | options?: object): boolean

Checks if input can be parsed as a "possible" phone number. A phone number is "possible" when it has valid length. The actual phone number digits aren't validated.

isPossiblePhoneNumber('8 (888) 888-88-88', 'RU') === true
isPossiblePhoneNumber('+12223333333') === true

For the description of the defaultCountry?: string | options?: object argument, see parsePhoneNumber() function description.

This function is just a shortcut for a two-step process of "strictly" parsing a phone number and then calling .isPossible().

isValidPhoneNumber(input: string, defaultCountry?: string | options?: object): boolean

Checks if input can be parsed as a "valid" phone number. A phone number is "valid" when it has valid length, and the actual phone number digits match the regular expressions for its country.

isValidPhoneNumber('8 (888) 888-88-88', 'RU') === false
isValidPhoneNumber('8 (800) 555-35-35', 'RU') === true
isValidPhoneNumber('+12223333333') === false
isValidPhoneNumber('+12133734253') === true

For the description of the defaultCountry?: string | options?: object argument, see parsePhoneNumber() function description.

This function is just a shortcut for a two-step process of "strictly" parsing a phone number and then calling .isValid().

See "Using phone number validation feature" for choosing between isPossible() and isValid().

isValidPhoneNumberForCountry(input: string, country: string): boolean

Same as isValidPhoneNumber() but with the "default country" argument being an "exact country" instead.

This function is not currently exported from this library. The reason is that its result would be too vague when false is returned — it could mean any of:

At least the second case should be handled separately from a "User Experience" point of view: if the user has input a valid phone number but for another country, they should be notified that "the country is incorrect" rather than that "the phone number is incorrect", otherwise it would be bad UX design.

But for those who'd still like to have such function, here's a possible implementation for it:

export default function isValidPhoneNumberForCountry(phoneNumberString, country) {
  const phoneNumber = parsePhoneNumber(phoneNumberString, {
    defaultCountry: country,
    // Demand that the entire input string must be a phone number.
    // Otherwise, it would "extract" a phone number from an input string.
    extract: false
  })
  if (!phoneNumber) {
    return false
  }
  if (phoneNumber.country !== country) {
    return false
  }
  return phoneNumber.isValid()
}

The same approach could be used to implement an isPossiblePhoneNumberForCountry() function.

validatePhoneNumberLength(input: string, defaultCountry?: string | options?: object): string?

Checks if input phone number length is valid. If it is, then nothing is returned. Otherwise, a rejection reason is returned.

<details> <summary>Possible rejection reasons</summary> </details>
validatePhoneNumberLength('abcde') === 'NOT_A_NUMBER'
validatePhoneNumberLength('444 1 44') === 'INVALID_COUNTRY'
validatePhoneNumberLength('444 1 44', 'TR') === 'TOO_SHORT'
validatePhoneNumberLength('444 1 444', 'TR') === undefined // Length is valid.
validatePhoneNumberLength('444 1 4444', 'TR') === 'INVALID_LENGTH'
validatePhoneNumberLength('444 1 44444', 'TR') === 'INVALID_LENGTH'
validatePhoneNumberLength('444 1 444444', 'TR') === undefined // Length is valid.
validatePhoneNumberLength('444 1 4444444444', 'TR') === 'TOO_LONG'

For the description of the defaultCountry?: string | options?: object argument, see parsePhoneNumber() function description.

This function is just a more detailed version of isPossiblePhoneNumber() for those who've asked for a more specific rejection reason.

<!-- This function is just a shortcut for a two-step process of ["strictly"](#strictness) parsing a phone number and then calling `.validateLength()`. -->

The phone number is parsed "strictly" from the input string.

class AsYouType(defaultCountry?: string | options?: object)

Creates a formatter for a partially entered phone number.

For the description of the defaultCountry?: string | options?: object argument, see parsePhoneNumber() function description.

The formatter instance has the following methods:

new AsYouType().input('+12133734') === '+1 213 373 4'
new AsYouType('US').input('2133734') === '(213) 373-4'

The formatter instance also provides the following getters:

// National phone number input example.

const asYouType = new AsYouType('US')

asYouType.input('2') === '2'
asYouType.getNumber().number === '+12'
asYouType.getChars() === '2'
asYouType.getTemplate() === 'x'

asYouType.input('1') === '21'
asYouType.getNumber().number === '+121'
asYouType.getChars() === '21'
asYouType.getTemplate() === 'xx'

asYouType.input('3') === '(213)'
asYouType.getNumber().number === '+1213'
asYouType.getChars() === '213'
asYouType.getTemplate() === '(xxx)'

asYouType.input('3734253') === '(213) 373-4253'
asYouType.getNumber().number === '+12133734253'
asYouType.getChars() === '2133734253'
asYouType.getTemplate() === '(xxx) xxx-xxxx'

// International phone number input example.

const asYouType = new AsYouType()
asYouType.input('+1-213-373-4253') === '+1 213 373 4253'
asYouType.getNumber().country === 'US'
asYouType.getNumber().number === '+12133734253'
asYouType.getChars() === '+12133734253'
asYouType.getTemplate() === 'xx xxx xxx xxxx'
<details> <summary>Legacy API (before version <code>1.6.0</code>)</summary>

For legacy API (before version 1.6.0) the formatter instance provides the following getters:

// National phone number input example.

const asYouType = new AsYouType('US')

asYouType.input('2') === '2'
asYouType.getNationalNumber() === '2'

asYouType.input('1') === '21'
asYouType.getNationalNumber() === '21'

asYouType.input('3') === '(213)'
asYouType.getNationalNumber() === '213'

asYouType.input('3734253') === '(213) 373-4253'
asYouType.getNationalNumber() === '2133734253'

// International phone number input example.

const asYouType = new AsYouType()
asYouType.input('+1-213-373-4253') === '+1 213 373 4253'
asYouType.country === 'US'
asYouType.getNationalNumber() === '2133734253'
</details>

"As You Type" formatter was created by Google as part of their Android OS and therefore only works for numerical keyboard input, i.e. it can only accept digits (and a + sign in the start of an international number). When used on desktops where a user can input all kinds of punctuation (spaces, dashes, parens, etc) it simply ignores everything except digits (and a + sign in the start of an international number).

Google's "As You Type" formatter does not support entering phone number extensions. If your project requires phone number extensions input then use a separate input field for that.

findPhoneNumbersInText(text: string, defaultCountry?: string | options?: object): object[]

Searches for phone numbers in text.

Available options:

For the description of defaultCountry or defaultCallingCode, see parsePhoneNumber() function description.

import { findPhoneNumbersInText } from 'libphonenumber-js'

findPhoneNumbersInText(`
  For tech support call +7 (800) 555-35-35 internationally
  or reach a local US branch at (213) 373-4253 ext. 1234.
`, 'US')

// Outputs:
//
// [{
//   number: PhoneNumber {
//     country: 'RU',
//     countryCallingCode: '7',
//     number: '+78005553535',
//     nationalNumber: '8005553535'
//   },
//   startsAt : 22,
//   endsAt   : 40
// }, {
//   number: PhoneNumber {
//     country: 'US',
//     countryCallingCode: '1',
//     number: '+12133734253',
//     nationalNumber: '2133734253',
//     ext: '1234'
//   },
//   startsAt : 86,
//   endsAt   : 110
// }]

(in previous versions, it was called findNumbers())

<details> <summary>Legacy API (before version <code>1.6.0</code>) example</summary>

import { findNumbers } from 'libphonenumber-js'

findNumbers(`
  For tech support call +7 (800) 555-35-35 internationally
  or reach a local US branch at (213) 373-4253 ext. 1234.
`, 'US')

// Outputs:
//
// [{
//   phone    : '8005553535',
//   country  : 'RU',
//   startsAt : 22,
//   endsAt   : 40
// },
// {
//   phone    : '2133734253',
//   country  : 'US',
//   ext      : '1234',
//   startsAt : 86,
//   endsAt   : 110
// }]
</details>

By default it processes the whole text and then outputs the phone numbers found. If the text is very big (say, a hundred thousand characters) then it might freeze the user interface for a couple of seconds. To avoid such lags one can employ "iterator" approach using searchPhoneNumbersInText() to perform the search asynchronously (e.g. using requestIdleCallback or requestAnimationFrame).

(in previous versions, it was called searchNumbers())

<details> <summary>Asynchronous search example using <code>searchPhoneNumbersInText()</code></summary>

ES6 iterator:

import { searchPhoneNumbersInText } from 'libphonenumber-js'

const text = `
  For tech support call +7 (800) 555-35-35 internationally
  or reach a local US branch at (213) 373-4253 ext. 1234.
`

async function() {
  for (const number of searchPhoneNumbersInText(text, 'US')) {
    console.log(number)
    await new Promise(resolve => setTimeout(resolve, 0))
  }
  console.log('Finished')
}

Java-style iterator (for those still not using ES6):

import { PhoneNumberMatcher } from 'libphonenumber-js'

const matcher = new PhoneNumberMatcher(`
  For tech support call +7 (800) 555-35-35 internationally
  or reach a local US branch at (213) 373-4253 ext. 1234.
`, {
  defaultCountry: 'US',
  v2: true
})

// Search cycle iteration.
const iteration = () => {
  if (matcher.hasNext()) {
    console.log(matcher.next())
    setTimeout(iteration, 0)
  } else {
    console.log('Finished')
  }
}

// Run the search.
iteration()
</details>

Although Google's javascript port doesn't have the findPhoneNumbersInText() functionality the Java and C++ ports do. I guess Google just doesn't need to crawl phone numbers on Node.js because they can afford to hire a Java/C++ developer to do that. Still, javascript nowadays is the most popular programming language given its simplicity and user-friendliness. The findPhoneNumbersInText() function provided is a port of Google's PhoneNumberMatcher.java into javascript.

getExampleNumber(country: string, examples: object): PhoneNumber

Returns an example phone number for a country. Returns an instance of PhoneNumber class. Will return undefined if country doesn't exist or isn't supported by this library.

import examples from 'libphonenumber-js/mobile/examples'
import { getExampleNumber } from 'libphonenumber-js'

const phoneNumber = getExampleNumber('RU', examples)

phoneNumber.formatNational() === '8 (912) 345-67-89'

isSupportedCountry(country: string): boolean

Checks if a country is supported by this library.

isSupportedCountry('RU') === true
isSupportedCountry('XX') === false

getCountries(): string[]

Returns a list of supported countries.

<!-- (excluding `"001"` that stands for ["non-geographic entity"](#non-geographic)). -->
getCountries() === ["AC", "AD", ...]

getCountryCallingCode(country: string): string

Returns country calling code for a country. Will throw an error if country doesn't exist or isn't supported by this library.

getCountryCallingCode('RU') === '7'
getCountryCallingCode('IL') === '972'

getExtPrefix(country: string): string

Returns phone number extension prefix for a given country. If no custom ext prefix is defined for a country then the default " ext. " prefix is returned.

getExtPrefix('US') === ' ext. '
getExtPrefix('GB') === ' x'

parseDigits(text: string): string

Parses digits from string. Can be used for building a phone number extension input component (e.g. react-phone-number-input).

parseDigits('x123') === '123'
parseDigits('٤٤٢٣') === '4423'

parseIncompletePhoneNumber(text: string): string

Parses phone number characters (+ and digits). Can be used for building a phone number input component (e.g. react-phone-number-input).

parseIncompletePhoneNumber('8 (800) 555') === '8800555'
parseIncompletePhoneNumber('+7 800 555') === '+7800555'
parseIncompletePhoneNumber('+٤٤٢٣٢٣٢٣٤') === '+442323234'

parsePhoneNumberCharacter(nextCharacter, prevParsedCharacters, emitEvent?)

Parses next character of an input string while parsing phone number digits (including a +) from that string. Basically, it discards everything except + and digits, and + is only allowed at the start of a phone number.

This function is a low-level one that is currently only used in react-phone-number-input with input-format. Frankly speaking, that's the only reason why this function is exported. Other developers should just ignore it and use parseIncompletePhoneNumber() instead because it's much simpler.

// Suppose a user inputs a "+1 (213) 373-42-53" string
// and it starts parsing that string character-by-character.

parsePhoneNumberCharacter('+', undefined) === '+'
parsePhoneNumberCharacter('1', '+') === '1'
parsePhoneNumberCharacter(' ', '+1') === undefined
parsePhoneNumberCharacter('(', '+1') === undefined
parsePhoneNumberCharacter('2', '+1') === '2'
parsePhoneNumberCharacter('1', '+12') === '1'
parsePhoneNumberCharacter('3', '+121') === '3'
parsePhoneNumberCharacter(')', '+1213') === undefined
parsePhoneNumberCharacter(' ', '+1213') === undefined
parsePhoneNumberCharacter('3', '+1213') === '3'
parsePhoneNumberCharacter('7', '+12133') === '7'
parsePhoneNumberCharacter('3', '+121337') === '3'
parsePhoneNumberCharacter('-', '+121337') === undefined
parsePhoneNumberCharacter('4', '+1213373') === '4'
parsePhoneNumberCharacter('2', '+12133734') === '2'
parsePhoneNumberCharacter('-', '+12133734') === undefined
parsePhoneNumberCharacter('5', '+121337342') === '5'
parsePhoneNumberCharacter('3', '+1213373425') === '3'

So basically, it's the same as parseIncompletePhoneNumber() with the only difference that it operates at a character-by-character level rather than at a string-as-a-whole level.

The optional emitEvent argument is a function of (eventName: string) arguments. It will be called in a situation when the application should stop parsing the input string. Currently, the only situation when that could happen is when it encounters an "out-of-place" + character. For example, when parsing a "+1 (234) + 56-78" string it would emit an "end" event at the second + character so that the application would return "+1234" rather than "+12345678".

formatIncompletePhoneNumber(value: string, defaultCountry?: string | options?: object): string

Formats a possibly incomplete phone number.

While the usual parsePhoneNumber(string).format() function could only be used to format a complete phone number, this function could be used to format a possibly incomplete phone number.

The value argument should be a (possibly incomplete) phone number in E.164 format.

For the description of the defaultCountry?: string | options?: object argument, see parsePhoneNumber() function description.

This function is just an alias for new AsYouType(defaultCountry, metadata).input(value). It can be used for building a phone number input component (e.g. react-phone-number-input).

// National numbers, with second argument.
formatIncompletePhoneNumber('8800555', 'RU') === '8 (800) 555'
formatIncompletePhoneNumber('8800555', { defaultCountry: 'RU' }) === '8 (800) 555'
formatIncompletePhoneNumber('8800555', { defaultCallingCode: '7' }) === '8 (800) 555'

// International numbers, without second argument.
formatIncompletePhoneNumber('+7800555') === '+7 800 555'

Legacy API

<details> <summary>Legacy API (before version <code>1.6.0</code>): <code>parse()</code>, <code>parseNumber()</code>, <code>format()</code>, <code>formatNumber()</code>, <code>isValidNumber()</code>, <code>getNumberType()</code>.</summary>

parseNumber(text, [defaultCountry], [options])

(previously called parse())

(legacy API)

Attempts to parse a phone number from text.

If defaultCountry is passed then it's gonna be the default country for parsing non-international phone numbers.

Returns { country, phone, ext } object where

If the phone number supplied isn't valid then an empty object {} is returned.

<details> <summary>Examples</summary>
// Parses international numbers.
parseNumber('+1 213 373 4253') === { country: 'US', phone: '2133734253' }
parseNumber('Phone: +1-213-373-4253.') === { country: 'US', phone: '2133734253' }
parseNumber('+12133734253') === { country: 'US', phone: '2133734253' }

// Parses national numbers provided a default country.
parseNumber('Phone: (213) 373-4253.', 'US') === { country: 'US', phone: '2133734253' }

// Parses phone number extensions.
parseNumber('(213) 373-4253 ext. 123', 'US') === { country: 'US', phone: '2133734253', ext: '123' }

// Parses RFC 3966 phone number URIs.
parseNumber('tel:+78005553535;ext=123') === { country: 'RU', phone: '8005553535', ext: '123' }

If the phone number supplied isn't valid then an empty object {} is returned.

parseNumber('+1 111 111 1111') === {}
parseNumber('(111) 111-1111', 'US') === {}
parseNumber('abcdefg') === {}
</details>

Available options:

<details> <summary><code>{ extended: true }</code> documentation and examples</summary>

The result of "extended" parsing is an object where

// If the number is valid.
parseNumber('Phone: (213) 373-4253.', 'US', { extended: true }) ===
{
  country: 'US',
  phone: '2133734253',
  ext: undefined,
  countryCallingCode: 1,
  carrierCode: undefined,
  valid: true,
  possible: true
}

// If the number is not "valid" but "possible".
parseNumber('(111) 111-1111', 'US', { extended: true }) ===
{
  country: 'US',
  phone: '1111111111',
  ext: undefined,
  countryCallingCode: 1,
  carrierCode: undefined,
  valid: false,
  possible: true
}

// If the number is not "valid" but "possible"
// and country can't be derived from it.
// (e.g. can't tell if it's a US number or a Canadian number)
parseNumber('+1 111 111 1111', { extended: true }) ===
{
  country: undefined,
  phone: '1111111111',
  ext: undefined,
  countryCallingCode: 1,
  carrierCode: undefined,
  valid: false,
  possible: true
}

// If the number is not "possible" (invalid length).
parseNumber('(213) 373', 'US', { extended: true }) ===
{
  country: 'US',
  phone: '213373',
  ext: undefined,
  countryCallingCode: 1,
  carrierCode: undefined,
  valid: false,
  possible: false
}

// In some cases if the number is extremely not "possible"
// then an empty object `{}` is returned.
//
// Too short (or too long) for any country's phone number.
parseNumber('1', 'US', { extended: true }) === {}
// Non-existent country calling code.
parseNumber('+210', { extended: true }) === {}
// No phone number found.
parseNumber('abcdefg', 'US', { extended: true }) === {}

The "extended" parsing mode is the default behaviour of the original Google's libphonenumber: it still returns parsed data even if the phone number being parsed is not considered valid (but is kinda "possible"). I guess this kind of behaviour is better for crawling websites for phone numbers because when mining "big data" it is better to extract all possible info rather than discard some pieces of it prematurely, e.g. when national (significant) number regexp for some country gets outdated which might very well happen because phone numbering plans are changing constantly around the world. Maybe after all it would make sense to make the "extended" parsing mode the default one in the next major version. I guess it would.

</details>

<details> <summary>Also parses IDD-prefixed phone numbers</summary>

Sometimes users icorrectly input phone numbers in "out-of-country" dialing (IDD-prefixed) format instead of the proper international phone number format (the "+" notation). In such cases parseNumber() will attempt to parse such IDD-prefixed numbers if "default country" is provided:

// International format.
parseNumber('+61 2 3456 7890') === { country: 'AU', phone: '234567890' }
// IDD-prefixed format.
parseNumber('011 61 2 3456 7890', 'US') === { country: 'AU', phone: '234567890' }
</details>

formatNumber(number, format, [options])

(previously called format())

(legacy API)

Formats a number into a string according to a format.

Available formats and options are the same as for PhoneNumber.format(format).

The number argument must be either a result of parseNumber() function call (to strip national prefix) or an E.164 phone number string (e.g. +12133734253).

<details> <summary>Examples</summary>
// Formats E.164 phone numbers.
formatNumber('+12133734253', 'NATIONAL') === '(213) 373-4253'
formatNumber('+12133734253', 'INTERNATIONAL') === '+1 213 373 4253'

// Formats E.164 phone numbers when
// they're not "valid" but still "possible".
formatNumber('+11111111111', 'NATIONAL') === '(111) 111-1111'
formatNumber('+11111111111', 'INTERNATIONAL') === '+1 111 111 1111'

// Formats E.164 phone numbers when
// they're not "valid" and not "possible" (invalid length).
formatNumber('+11111', 'NATIONAL') === '1111'
formatNumber('+11111', 'INTERNATIONAL') === '+1 1111'

// Formats a result of `parseNumber()` function call.
const parsedNumber = parseNumber('2133734253', 'US')
formatNumber(parsedNumber, 'NATIONAL') === '(213) 373-4253'
formatNumber(parsedNumber, 'INTERNATIONAL') === '+1 213 373 4253'

// Formats a result of `parseNumber()` function call in "extended" mode
// when it's not a "valid" number but is still a "possible" one.
const possibleNumber = parseNumber('+11111111111', { extended: true })
formatNumber(possibleNumber, 'NATIONAL') === '(111) 111-1111'
formatNumber(possibleNumber, 'INTERNATIONAL') === '+1 111 111 1111'

// Formats a result of `parseNumber()` function call in "extended" mode
// when it's neither a "valid" number nor a "possible" one (invalid length).
const possibleNumber = parseNumber('+11111', { extended: true })
formatNumber(possibleNumber, 'NATIONAL') === '1111'
formatNumber(possibleNumber, 'INTERNATIONAL') === '+1 1111'

// Formats phone number extensions.
formatNumber({ country: 'US', phone: '2133734253', ext: '123' }, 'NATIONAL') ===  '(213) 373-4253 ext. 123'

// When given an object not having `phone` property
// (e.g. a empty object `{}`) it will throw.
formatNumber({}) throws Error
</details>

getNumberType(number, [defaultCountry])

(legacy API)

See the description for PhoneNumber.getType().

The number argument can be either a result of the parseNumber() function call — { country, phone } — or a string (phone number digits only) possibly accompanied with the second defaultCountry argument.

<details> <summary>Examples</summary>
getNumberType('+79160151539') === 'MOBILE'
getNumberType('9160151539', 'RU') === 'MOBILE'
getNumberType({ phone: '9160151539', country: 'RU' }) === 'MOBILE'
</details>

isValidNumber(number, [defaultCountry])

(legacy API)

Checks if a phone number is valid, the validation is more strict than parseNumber().

The number argument can be either a result of the parseNumber() function call — { country, phone } — or a string (phone number digits only) possibly accompanied with the second defaultCountry argument.

<details> <summary>Examples</summary>
isValidNumber('+12133734253') === true
isValidNumber('+1213373') === false

isValidNumber('2133734253', 'US') === true
isValidNumber('21337', 'US') === false

isValidNumber({ phone: '2133734253', country: 'US' }) === true
</details>

<details> <summary>The difference between using <code>parseNumber()</code> and <code>isValidNumber()</code></summary>

The difference between using parseNumber() and isValidNumber() for phone number validation is that isValidNumber() also checks the precise regular expressions of possible phone numbers for a country. For example, for Germany parseNumber('123456', 'DE') would return { country: 'DE', phone: '123456' } because this phone number matches the general phone number rules for Germany (basic length check, etc). But, if the metadata is compiled with --extended (or relevant --types) flag (see Customizing metadata section of this document) then isValidNumber() is gonna use those precise regular expressions for extensive validation and isValid('123456', 'DE') will return false because the phone number 123456 doesn't actually exist in Germany.

This is how it is implemented in the original Google's libphonenumber: parseNumber() parses phone numbers and loosely validates them while isValidNumber() validates phone numbers precisely (provided the precise regular expressions are included in metadata).

The precise regular expressions aren't included in the default metadata because that would cause the default metadata to grow twice in its size: the complete ("full") metadata size is about 145 kilobytes while the reduced ("default") metadata size is about 77 kilobytes. Hence in the default configuration isValidNumber() performs absolutely the same "lite" validation as parseNumber(). For enabling extensive phone number validation the simplest way is to import functions from libphonenumber-js/custom module and supply them with libphonenumber-js/metadata.max.json. For generating custom metadata see the instructions provided in the Customizing metadata section of this document.

</details>

<details> <summary><code>isValidNumberForRegion()</code></summary>

The optional defaultCountry argument is the default country, i.e. it does not restrict to just that country, e.g. in those cases where several countries share the same phone numbering rules (NANPA, Britain, etc). For example, even though the number 07624 369230 belongs to the Isle of Man ("IM" country code) calling isValidNumber('07624369230', 'GB') still returns true because the country is not restricted to GB, it's just that GB is the default one for the phone numbering rules. For restricting the country, see isValidNumberForRegion(), though restricting a country might not be a good idea.

// Even though '07624 369230' number belongs to the Isle of Man ("IM")
// the `defaultCountry` argument "GB" still works here because
// "GB" and "IM" both share the same phone numbering rules ("+44").
isValidNumber('07624369230', 'GB') === true
isValidNumber('07624369230', 'IM') === true

// Imposing country restrictions.
isValidNumberForRegion('07624369230', 'GB') === false
isValidNumberForRegion('07624369230', 'IM') === true
</details> </details>

Using phone number validation feature

I personally don't use strict phone number validation in my projects. The rationale is that telephone numbering plans can and sometimes do change, meaning that PhoneNumber.isValid() function may one day become outdated on a website that isn't actively maintained anymore. Imagine a "promo-site" or a "personal website" being deployed once and then running for years without any maintenance, where a client may be unable to submit a simple "Contact Us" form just because this newly allocated pool of mobile phone numbers wasn't present in that old version of libphonenumber-js bundled in it.

Whenever there's a "business requirement" to validate a phone number that's being input by a user, I prefer using PhoneNumber.isPossible() instead of PhoneNumber.isValid(), so that it just validates the phone number length, and doesn't validate the actual phone number digits. But it doesn't mean that you shouldn't use PhoneNumber.isValid() — maybe in your case it would make sense.

React

If you’re trying to build a React component with this library, take a look at react-phone-number-input.

<!-- ## Node ES Modules This package currently [doesn't provide](https://gitlab.com/catamphetamine/libphonenumber-js/-/tree/master/exports) Node 14+ ES Modules exports, but that shouldn't be an issue because Node.js seems to [support](https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/42) importing from "legacy" packages just fine. -->

Bug reporting

This library is a simpler rewrite of Google's libphonenumber library. Basically, it mimicks Google's library behavior. Hence, as long as this library's demo page and Google's library demo page exhibit the same behavior, it's not considered a bug.

When reporting issues with:

one must provide two links:

Google's demo page outputs four sections:

Choose the relevant section of those four corresponding to the type of the bug you're reporting.

In a bug report:

<!-- Phone number validation bugs should **only** be reported if they appear when using [custom metadata functions](#customizing-metadata) fed with `metadata.max.json` because by default all functions in this library use the reduced metadata set which results in looser validation than the original Google `libphonenumber`'s. The [demo page](https://catamphetamine.gitlab.io/libphonenumber-js/) also uses the reduced metadata set and therefore its validation is also looser than the original Google `libphonenumber`'s. There is also a possibility of this library's demo metadata being outdated, or this library's metadata lagging behind Google's (I have to update it manually from time to time due to `ssh-agent` not working properly on Windows). --> <!-- When reporting `findPhoneNumbersInText()` bugs, one should know that `findPhoneNumbersInText()` code was ported from [Google's Java code](https://github.com/googlei18n/libphonenumber/blob/master/java/libphonenumber/src/com/google/i18n/phonenumbers/PhoneNumberMatcher.java). I didn't write it myself, I just ported it. Therefore, it is unlikely that anyone other than Google would fix any bugs. -->

TypeScript

This library comes with TypeScript "typings". If you happen to find any bugs in those, create an issue.

<!-- https://codesandbox.io/s/damp-fast-26c7z?file=/src/index.ts -->

CDN

One can use any npm CDN service, e.g. unpkg.com or jsdelivr.com

<script src="https://unpkg.com/libphonenumber-js@[version]/bundle/libphonenumber-[type].js"></script>

<script>
  alert(new libphonenumber.AsYouType('US').input('213-373-4253'))
</script>

where [version] is an npm package version range (for example, 1.x or ^1.7.6) and [type] is the bundle type: min, max or mobile.

<!-- ## Standalone For those who aren't using bundlers for some reason there's a way to build a standalone version of the library * `git clone https://gitlab.com/catamphetamine/libphonenumber-js.git` * `npm install` * `npm run build` * See the `bundle` folder for `libphonenumber-js.min.js` ```html <script src="/scripts/libphonenumber-js.min.js"></script> <script> alert(new libphonenumber.AsYouType('US').input('213-373-4253')) </script> ``` -->

Metadata

Metadata is generated from Google's PhoneNumberMetadata.xml by transforming XML into JSON and removing unnecessary fields. See metadata fields description.

Programmatic access

Metadata can be accessed programmatically by using the exported Metadata class.

First, create a Metadata class instance:

import { Metadata } from 'libphonenumber-js'

const metadata = new Metadata()

Then, select a "numbering plan" (a country):

metadata.selectNumberingPlan('US')

After that, the following methods of metadata.numberingPlan can be called:

Example:

import { Metadata } from 'libphonenumber-js'

const metadata = new Metadata()
metadata.selectNumberingPlan('US')

metadata.numberingPlan.leadingDigits() === undefined
metadata.numberingPlan.possibleLengths() === [10]
metadata.numberingPlan.IDDPrefix() === '011'
metadata.numberingPlan.defaultIDDPrefix() === undefined

Using with custom metadata:

import { Metadata } from 'libphonenumber-js/core'

import min from 'libphonenumber-js/min/metadata'
// import max from 'libphonenumber-js/max/metadata'
// import mobile from 'libphonenumber-js/mobile/metadata'

const metadata = new Metadata(min)

As one can see, the Metadata class is not documented much. Partially, that's because its usage is not necessarily encouraged, but it's still used, for example, in react-phone-number-input to get "leading digits" for a country, or to get maximum phone number length for a country. Stick to the methods documented above, don't call any other methods. If you think there's a need to call any other methods, create a discussion issue.

<!-- Currently I have a script set up monitoring changes to `PhoneNumberMetadata.xml` in Google's repo and automatically releasing new versions of this library when metadata in Google's repo gets updated. So this library's metadata is supposed to be up-to-date. Still, in case the automatic metadata update script malfunctions some day, anyone can request metadata update via a Pull Request here on GitHub: --> <!-- In case I forget to run the "autoupdate" script for a long time anyone can request metadata update via a Pull Request here on GitHub: * Fork this repo * `npm install` * `npm run metadata:update:branch` * Submit a Pull Request to this repo from the `update-metadata` branch of your fork `npm run metadata:update:branch` command creates a new `update-metadata` branch, downloads the new [`PhoneNumberMetadata.xml`](https://github.com/googlei18n/libphonenumber/blob/master/resources/PhoneNumberMetadata.xml) into the project folder replacing the old one, generates JSON metadata out of the XML one, checks if the metadata has changed, runs the tests, commits the new metadata and pushes the commit to the remote `update-metadata` branch of your fork. Alternatively, a developer may wish to update metadata urgently, without waiting for a pull request approval. In this case just perform the steps described in the [Customizing metadata](#customizing-metadata) section of this document. -->

Customizing metadata

This library comes prepackaged with three types of metadata.

Sometimes, if only a specific set of countries is needed in a project, and a developer really wants to reduce the resulting bundle size, say, by 50 kilobytes (even when including all regular expressions for validating phone number digits and detecting phone number type), then they can generate such custom metadata and pass it as the last argument to this library's "core" functions.

See generate custom metadata instructions.

<details> <summary>Then, use the generated <code>metadata.custom.json</code> file with the "core" functions.</summary>

Pass the metadata argument as the last one to the "core" functions.

In ES6 that would be:

import _parsePhoneNumber, {
  findPhoneNumbersInText as _findPhoneNumbersInText,
  AsYouType as _AsYouType
} from 'libphonenumber-js/core'

import metadata from 'libphonenumber-js/max/metadata'

function call(func, _arguments) {
  var args = Array.prototype.slice.call(_arguments)
  args.push(metadata)
  return func.apply(this, args)
}

export default function parsePhoneNumber() {
  return call(_parsePhoneNumber, arguments)
}

export function findPhoneNumbersInText() {
  return call(_findPhoneNumbersInText, arguments)
}

export function AsYouType(country) {
  return _AsYouType.call(this, country, metadata)
}
AsYouType.prototype = Object.create(_AsYouType.prototype, {})
AsYouType.prototype.constructor = AsYouType

And for Common.js environment that would be:

var core = require('libphonenumber-js/core')
var metadata = require('libphonenumber-js/max/metadata')

function call(func, _arguments) {
  var args = Array.prototype.slice.call(_arguments)
  args.push(metadata)
  return func.apply(this, args)
}

function parsePhoneNumber() {
  return call(core.default, arguments)
}

exports = module.exports = parsePhoneNumber
exports['default'] = parsePhoneNumber

exports.findPhoneNumbersInText = function findPhoneNumbersInText() {
  return call(core.findPhoneNumbersInText, arguments)
}

exports.AsYouType = function AsYouType(country) {
  return core.AsYouType.call(this, country, metadata)
}
exports.AsYouType.prototype = Object.create(core.AsYouType.prototype, {})
exports.AsYouType.prototype.constructor = exports.AsYouType
</details>

<details> <summary>Legacy: How to use the generated <code>metadata.custom.json</code> file with the legacy "custom" functions.</summary>

Pass the metadata argument as the last one to the "custom" functions.

In ES6 that would be:

import {
  parseNumber,
  formatNumber,
  isValidNumber,
  getNumberType,
  AsYouType
} from 'libphonenumber-js/custom'

import metadata from 'libphonenumber-js/max/metadata'

parseNumber('+78005553535', metadata)
formatNumber({ phone: '8005553535', country: 'RU' }, metadata)
isValidNumber('+78005553535', metadata)
getNumberType('+78005553535', metadata)
new AsYouType('RU', metadata).input('+78005553535')

And for Common.js environment that would be:

var custom = require('libphonenumber-js/custom')
var metadata = require('libphonenumber-js/max/metadata')

exports.parseNumber = function parseNumber() {
  var parameters = Array.prototype.slice.call(arguments)
  parameters.push(metadata)
  return custom.parseNumber.apply(this, parameters)
}

exports.formatNumber = function formatNumber() {
  var parameters = Array.prototype.slice.call(arguments)
  parameters.push(metadata)
  return custom.formatNumber.apply(this, parameters)
}

exports.isValidNumber = function isValidNumber() {
  var parameters = Array.prototype.slice.call(arguments)
  parameters.push(metadata)
  return custom.isValidNumber.apply(this, parameters)
}

exports.getNumberType = function isValidNumber() {
  var parameters = Array.prototype.slice.call(arguments)
  parameters.push(metadata)
  return custom.getNumberType.apply(this, parameters)
}

exports.AsYouType = function AsYouType(country) {
  custom.AsYouType.call(this, country, metadata)
}

exports.AsYouType.prototype = Object.create(custom.AsYouType.prototype, {})
exports.AsYouType.prototype.constructor = exports.AsYouType
</details>

Metadata should be re-generated each time the project is being deployed because Google constantly updates their metadata.

<!-- ## To do --> <!-- ## Automatic metadata update setup Create a daily (24 * 60 * 60) `launchd` job http://alvinalexander.com/mac-os-x/mac-osx-startup-crontab-launchd-jobs ```sh mkdir /Users/kuchumovn/work/libphonenumber-js-autoupdate git clone https://gitlab.com/catamphetamine/libphonenumber-js.git /Users/kuchumovn/work/libphonenumber-js-autoupdate cd /Users/kuchumovn/work/libphonenumber-js-autoupdate npm install chmod u+x /Users/kuchumovn/work/libphonenumber-js-autoupdate/autoupdate.sh nano ~/Library/LaunchAgents/com.gitlab.catamphetamine.libphonenumber-js.metadata-update.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.gitlab.catamphetamine.libphonenumber-js.metadata-update</string> <key>ProgramArguments</key> <array> <string>/Users/kuchumovn/work/libphonenumber-js-autoupdate/autoupdate.sh</string> </array> <key>Nice</key> <integer>1</integer> <key>StartInterval</key> <integer>86400</integer> <key>RunAtLoad</key> <true/> <key>StandardErrorPath</key> <string>/tmp/libphonenumber.errors.txt</string> <key>StandardOutPath</key> <string>/tmp/libphonenumber.output.txt</string> </dict> </plist> launchctl load ~/Library/LaunchAgents/com.gitlab.catamphetamine.libphonenumber-js.metadata-update.plist launchctl list | grep 'libphonenumber-js' ``` -->

Maintenance

Google periodically releases new metadata with the changes described in the release notes. Sometimes those are minor non-breaking updates, sometimes those are major-version breaking updates.

Metadata update process is automated through an "autoupdate" script: see autoupdate.cmd (Windows) or autoupdate.sh (Linux/macOS). The script detects changes to PhoneNumberMetadata.xml in Google libphonenumber's repo and if there are changes then it pulls the latest metadata, processes it, commits the changes to GitHub, builds a new version of the library and releases it to NPM. So this library's metadata is supposed to be up-to-date. I could set up this script to run automatically but on my Windows machine ssh-agent doesn't work properly so I run the "autoupdate" script manually from time to time.

Also Google sometimes (very rarely) updates their code:

The latest sync-up was on Aug 14th, 2024.

Contributing

After cloning this repo, ensure dependencies are installed by running:

npm install

This module is written in ES6 and uses Babel for ES5 transpilation. Widely consumable JavaScript can be produced by running:

npm run build

Once npm run build has run, you may import or require() directly from node.

After developing, the full test suite can be evaluated by running:

npm test

Test coverage must remain at 100%:

npm run test-coverage

When you're ready to test your new functionality on a real project, you can run

npm pack

It will build, test and then create a .tgz archive which you can then install in your project folder

npm install [module name with version].tar.gz

Tests

This component comes with a 100% code coverage.

To run tests:

npm test

To generate a code coverage report:

npm run test-coverage

The code coverage report can be viewed by opening ./coverage/lcov-report/index.html.

Advertisement

If you like this library then you might also like:

License

Google's libphonenumber is licensed under Apache 2.

Apache 2 does not require a derivative work of the software, or modifications to the original, to be distributed using the same license. Hence, this library is licensed under MIT, which is compatible with Apache 2.

The Apache license is terminated if the user sues anyone over patent infringement related to the software covered by the license. This condition is added in order to prevent patent litigations.