libphonenumber-js
Version:
A simpler (and smaller) rewrite of Google Android's popular libphonenumber library
234 lines (203 loc) • 9.71 kB
JavaScript
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
// This is a port of Google Android `libphonenumber`'s
// `phonenumberutil.js` of 17th November, 2016.
//
// https://github.com/googlei18n/libphonenumber/commits/master/javascript/i18n/phonenumbers/phonenumberutil.js
import { VALID_PUNCTUATION } from './common.constants';
import { matches_entirely } from './common';
import Metadata from './metadata';
import { getIDDPrefix } from './IDD';
import { formatRFC3966 } from './RFC3966';
var defaultOptions = {
formatExtension: function formatExtension(number, extension, metadata) {
return '' + number + metadata.ext() + extension;
}
// Formats a phone number
//
// Example use cases:
//
// ```js
// formatNumber('8005553535', 'RU', 'INTERNATIONAL')
// formatNumber('8005553535', 'RU', 'INTERNATIONAL', metadata)
// formatNumber({ phone: '8005553535', country: 'RU' }, 'INTERNATIONAL')
// formatNumber({ phone: '8005553535', country: 'RU' }, 'INTERNATIONAL', metadata)
// formatNumber('+78005553535', 'NATIONAL')
// formatNumber('+78005553535', 'NATIONAL', metadata)
// ```
//
};export default function formatNumber(input, format, options, metadata) {
// Apply default options.
if (options) {
options = _extends({}, defaultOptions, options);
} else {
options = defaultOptions;
}
metadata = new Metadata(metadata);
if (input.country) {
// Validate `input.country`.
if (!metadata.hasCountry(input.country)) {
throw new Error('Unknown country: ' + input.country);
}
metadata.country(input.country);
} else if (input.countryCallingCode) {
metadata.chooseCountryByCountryCallingCode(input.countryCallingCode);
} else return input.phone || '';
var countryCallingCode = metadata.countryCallingCode();
var nationalNumber = options.v2 ? input.nationalNumber : input.phone;
// This variable should have been declared inside `case`s
// but Babel has a bug and it says "duplicate variable declaration".
var number = void 0;
switch (format) {
case 'INTERNATIONAL':
// Legacy argument support.
// (`{ country: ..., phone: '' }`)
if (!nationalNumber) {
return '+' + countryCallingCode;
}
number = format_national_number(nationalNumber, 'INTERNATIONAL', metadata);
number = '+' + countryCallingCode + ' ' + number;
return add_extension(number, input.ext, metadata, options.formatExtension);
case 'E.164':
// `E.164` doesn't define "phone number extensions".
return '+' + countryCallingCode + nationalNumber;
case 'RFC3966':
return formatRFC3966({
number: '+' + countryCallingCode + nationalNumber,
ext: input.ext
});
case 'IDD':
if (!options.fromCountry) {
return;
// throw new Error('`fromCountry` option not passed for IDD-prefixed formatting.')
}
var IDDPrefix = getIDDPrefix(options.fromCountry, metadata.metadata);
if (!IDDPrefix) {
return;
}
if (options.humanReadable) {
var formattedForSameCountryCallingCode = countryCallingCode && formatIDDSameCountryCallingCodeNumber(nationalNumber, metadata.countryCallingCode(), options.fromCountry, metadata);
if (formattedForSameCountryCallingCode) {
number = formattedForSameCountryCallingCode;
} else {
number = IDDPrefix + ' ' + countryCallingCode + ' ' + format_national_number(nationalNumber, 'INTERNATIONAL', metadata);
}
return add_extension(number, input.ext, metadata, options.formatExtension);
}
return '' + IDDPrefix + countryCallingCode + nationalNumber;
case 'NATIONAL':
// Legacy argument support.
// (`{ country: ..., phone: '' }`)
if (!nationalNumber) {
return '';
}
number = format_national_number(nationalNumber, 'NATIONAL', metadata);
return add_extension(number, input.ext, metadata, options.formatExtension);
default:
throw new Error('Unknown "format" argument passed to "formatNumber()": "' + format + '"');
}
}
// This was originally set to $1 but there are some countries for which the
// first group is not used in the national pattern (e.g. Argentina) so the $1
// group does not match correctly. Therefore, we use \d, so that the first
// group actually used in the pattern will be matched.
export var FIRST_GROUP_PATTERN = /(\$\d)/;
export function format_national_number_using_format(number, format, useInternationalFormat, includeNationalPrefixForNationalFormat, metadata) {
var formattedNumber = number.replace(new RegExp(format.pattern()), useInternationalFormat ? format.internationalFormat() : format.nationalPrefixFormattingRule() && (!format.nationalPrefixIsOptionalWhenFormatting() || includeNationalPrefixForNationalFormat) ? format.format().replace(FIRST_GROUP_PATTERN, format.nationalPrefixFormattingRule()) : format.format());
if (useInternationalFormat) {
return changeInternationalFormatStyle(formattedNumber);
}
return formattedNumber;
}
function format_national_number(number, format_as, metadata) {
var format = choose_format_for_number(metadata.formats(), number);
if (!format) {
return number;
}
return format_national_number_using_format(number, format, format_as === 'INTERNATIONAL', true, metadata);
}
export function choose_format_for_number(available_formats, national_number) {
for (var _iterator = available_formats, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
var _ref;
if (_isArray) {
if (_i >= _iterator.length) break;
_ref = _iterator[_i++];
} else {
_i = _iterator.next();
if (_i.done) break;
_ref = _i.value;
}
var format = _ref;
// Validate leading digits
if (format.leadingDigitsPatterns().length > 0) {
// The last leading_digits_pattern is used here, as it is the most detailed
var last_leading_digits_pattern = format.leadingDigitsPatterns()[format.leadingDigitsPatterns().length - 1];
// If leading digits don't match then move on to the next phone number format
if (national_number.search(last_leading_digits_pattern) !== 0) {
continue;
}
}
// Check that the national number matches the phone number format regular expression
if (matches_entirely(national_number, format.pattern())) {
return format;
}
}
}
// Removes brackets and replaces dashes with spaces.
//
// E.g. "(999) 111-22-33" -> "999 111 22 33"
//
// For some reason Google's metadata contains `<intlFormat/>`s with brackets and dashes.
// Meanwhile, there's no single opinion about using punctuation in international phone numbers.
//
// For example, Google's `<intlFormat/>` for USA is `+1 213-373-4253`.
// And here's a quote from WikiPedia's "North American Numbering Plan" page:
// https://en.wikipedia.org/wiki/North_American_Numbering_Plan
//
// "The country calling code for all countries participating in the NANP is 1.
// In international format, an NANP number should be listed as +1 301 555 01 00,
// where 301 is an area code (Maryland)."
//
// I personally prefer the international format without any punctuation.
// For example, brackets are remnants of the old age, meaning that the
// phone number part in brackets (so called "area code") can be omitted
// if dialing within the same "area".
// And hyphens were clearly introduced for splitting local numbers into memorizable groups.
// For example, remembering "5553535" is difficult but "555-35-35" is much simpler.
// Imagine a man taking a bus from home to work and seeing an ad with a phone number.
// He has a couple of seconds to memorize that number until it passes by.
// If it were spaces instead of hyphens the man wouldn't necessarily get it,
// but with hyphens instead of spaces the grouping is more explicit.
// I personally think that hyphens introduce visual clutter,
// so I prefer replacing them with spaces in international numbers.
// In the modern age all output is done on displays where spaces are clearly distinguishable
// so hyphens can be safely replaced with spaces without losing any legibility.
//
export function changeInternationalFormatStyle(local) {
return local.replace(new RegExp('[' + VALID_PUNCTUATION + ']+', 'g'), ' ').trim();
}
function add_extension(number, ext, metadata, formatExtension) {
return ext ? formatExtension(number, ext, metadata) : number;
}
export function formatIDDSameCountryCallingCodeNumber(number, toCountryCallingCode, fromCountry, toCountryMetadata) {
var fromCountryMetadata = new Metadata(toCountryMetadata.metadata);
fromCountryMetadata.country(fromCountry);
// If calling within the same country calling code.
if (toCountryCallingCode === fromCountryMetadata.countryCallingCode()) {
// For NANPA regions, return the national format for these regions
// but prefix it with the country calling code.
if (toCountryCallingCode === '1') {
return toCountryCallingCode + ' ' + format_national_number(number, 'NATIONAL', toCountryMetadata);
}
// If regions share a country calling code, the country calling code need
// not be dialled. This also applies when dialling within a region, so this
// if clause covers both these cases. Technically this is the case for
// dialling from La Reunion to other overseas departments of France (French
// Guiana, Martinique, Guadeloupe), but not vice versa - so we don't cover
// this edge case for now and for those cases return the version including
// country calling code. Details here:
// http://www.petitfute.com/voyage/225-info-pratiques-reunion
//
return format_national_number(number, 'NATIONAL', toCountryMetadata);
}
}
//# sourceMappingURL=format_.js.map