-module(phonenumber_util). -include("libphonenumber.hrl"). -define(NOT_LOADED, not_loaded(?LINE)). -on_load(load_nif/0). -export([ % geocoder get_geocoding_for_number/2, % utils get_supported_regions/0, is_alpha_number/1, convert_alpha_characters_in_number/1, normalize_digits_only/1, normalize_diallable_chars_only/1, get_national_significant_number/1, get_length_of_geograpical_area_code/1, get_length_of_national_destination_code/1, get_country_mobile_token/1, format/2, format_by_pattern/3, format_national_number_with_carrier_code/2, format_national_number_with_preferred_carrier_code/2, format_number_for_mobile_dialing/3, format_out_of_country_calling_number/2, format_in_original_format/2, format_out_of_country_keeping_alpha_chars/2, truncate_too_long_number/1, get_number_type/1, is_valid_number/1, is_valid_number_for_region/2, get_region_code_for_number/1, get_country_code_for_region/1, get_region_code_for_country_code/1, get_region_codes_for_country_calling_code/1, get_region_display_name/2, is_nanpa_country/1, get_ndd_prefix_for_region/2, is_possible_number_with_reason/1, is_possible_number/1, is_possible_number_for_string/2, can_be_internationally_dialled/1, is_number_geographical/1, get_example_number/1, get_example_number_for_type/2, get_example_number_for_non_geo_entity/1, parse/2, parse_and_keep_raw_input/2, is_number_match/2, is_number_match_with_two_strings/2, is_number_match_with_one_string/2 ]). load_nif() -> SoName = elibphone_utils:get_priv_path(<<(atom_to_binary(?MODULE, latin1))/binary, "_nif">>), ok = erlang:load_nif(SoName, 0). not_loaded(Line) -> erlang:nif_error({not_loaded, [{module, ?MODULE}, {line, Line}]}). %% @doc Returns a text description for the given phone number, in the language %% provided. The description might consist of the name of the country where %% the phone number is from, or the name of the geographical area the phone %% number is from if more detailed information is available. Returns an empty %% string if the number could come from multiple countries, or the country %% code is in fact invalid. %% %% This method assumes the validity of the number passed in has already been %% checked, and that the number is suitable for geocoding. We consider %% fixed-line and mobile numbers possible candidates for geocoding. -spec get_geocoding_for_number(Number::phonenumber(), Locale::binary()) -> binary(). get_geocoding_for_number(_Number, _Locale) -> ?NOT_LOADED. -spec get_supported_regions() -> [binary()]. %% @doc Convenience method to get a list of what regions the library has metadata for. get_supported_regions() -> ?NOT_LOADED. -spec is_alpha_number(Number::binary()) -> boolean(). %% @doc Returns true if the number is a valid vanity (alpha) number such as 800 %% MICROSOFT. A valid vanity number will start with at least 3 digits and will %% have three or more alpha characters. This does not do region-specific %% checks - to work out if this number is actually valid for a region, it %% should be parsed and methods such as IsPossibleNumberWithReason or %% IsValidNumber should be used. is_alpha_number(_Number) -> ?NOT_LOADED. -spec convert_alpha_characters_in_number(Number::binary()) -> binary(). %% @doc Converts all alpha characters in a number to their respective digits on %% a keypad, but retains existing formatting. convert_alpha_characters_in_number(_Number) -> ?NOT_LOADED. -spec normalize_digits_only(Number::binary()) -> binary(). %% @doc Normalizes a string of characters representing a phone number. This %% converts wide-ascii and arabic-indic numerals to European numerals, and %% strips punctuation and alpha characters. normalize_digits_only(_Number) -> ?NOT_LOADED. -spec normalize_diallable_chars_only(Number::binary()) -> binary(). %% @doc Normalizes a string of characters representing a phone number. This strips %% all characters which are not diallable on a mobile phone keypad (including %% all non-ASCII digits). normalize_diallable_chars_only(_Number) -> ?NOT_LOADED. -spec get_national_significant_number(PhoneNumber::phonenumber()) -> NationalSignificantNum::binary(). %% @doc Gets the national significant number of a phone number. Note a national %% significant number doesn't contain a national prefix or any formatting. get_national_significant_number(_PhoneNumber) -> ?NOT_LOADED. -spec get_length_of_national_destination_code(PhoneNumber::phonenumber()) -> non_neg_integer(). %% @doc Gets the length of the national destination code (NDC) from the PhoneNumber %% object passed in, so that clients could use it to split a national %% significant number into NDC and subscriber number. The NDC of a phone %% number is normally the first group of digit(s) right after the country %% calling code when the number is formatted in the international format, if %% there is a subscriber number part that follows. %% ``` %% PhoneNumber = phonenumber_util:parse(<<"16502530000">>, <<"US">>), %% NationalSignificantNumber = phonenumber_util:get_national_significant_number(PhoneNumber), %% NationalDestinationCodeLength = phonenumber_util:get_length_of_national_destination_code(PhoneNumber), %% {NationalDestinationCode, SubscriberNumber} = if %% NationalDestinationCodeLength > 0 -> %% {binary:part(NationalSignificantNumber, NationalDestinationCodeLength), %% binary:part(NationalSignificantNumber, NationalDestinationCodeLength, byte_size(NationalSignificantNumber))}; %% true -> %% {<<>>, NationalSignificantNumber} %% end. %% ''' get_length_of_national_destination_code(_PhoneNumber) -> ?NOT_LOADED. -spec get_length_of_geograpical_area_code(PhoneNumber::phonenumber()) -> non_neg_integer(). %% @doc Gets the length of the geographical area code from the PhoneNumber object %% passed in, so that clients could use it to split a national significant %% number into geographical area code and subscriber number. It works in such %% a way that the resultant subscriber number should be diallable, at least on %% some devices. An example of how this could be used: %% %% ``` %% PhoneNumber = phonenumber_util.parse(<<"16502530000">>,<<"US">>), %% NationalSignificantNumber = phonenumber_util.get_national_significant_number(PhoneNumber), %% AreaCodeLength = phonenumber_util.get_lenth_of_geographical_area_code(PhoneNumber), %% {AreaCode, SubscriberNumber} = if %% AreaCodeLength > 0 -> %% {binary:part(NationalSignificantNumber, AreaCodeLength), %% binary:part(NationalSignificantNumber, AreaCodeLength, byte_size(NationalSignificantNumber))}; %% true -> %% {<<>>, NationalSignificatNumber} %% end. %% ''' %% %% N.B.: area code is a very ambiguous concept, so the authors generally %% recommend against using it for most purposes, but recommend using the %% more general national_number instead. Read the following carefully before %% deciding to use this method: %% %% - geographical area codes change over time, and this method honors those %% changes; therefore, it doesn't guarantee the stability of the result it %% produces. %% %% - subscriber numbers may not be diallable from all devices (notably mobile %% devices, which typically requires the full national_number to be dialled %% in most regions). %% %% - most non-geographical numbers have no area codes, including numbers %% from non-geographical entities. %% %% - some geographical numbers have no area codes. get_length_of_geograpical_area_code(_PhoneNumber) -> ?NOT_LOADED. -spec get_country_mobile_token(CountryCallingCode::non_neg_integer()) -> binary(). %% @doc Returns the mobile token for the provided country calling code if it has %% one, otherwise returns an empty string. A mobile token is a number inserted %% before the area code when dialing a mobile number from that country from %% abroad. get_country_mobile_token(_CountryCallingCode) -> ?NOT_LOADED. -spec format(PhoneNumber::phonenumber(), PhoneNumberFormat::phonenumber_format()) -> FormattedNumber::binary(). %% @doc Formats a phone number in the specified format using default rules. Note %% that this does not promise to produce a phone number that the user can %% dial from where they are - although we do format in either NATIONAL or %% INTERNATIONAL format depending on what the client asks for, we do not %% currently support a more abbreviated format, such as for users in the %% same area who could potentially dial the number without area code. format(_PhoneNumber, _PhoneNumberFormat) -> ?NOT_LOADED. -spec format_by_pattern(PhoneNumber::phonenumber(), PhoneNumberFormat::phonenumber_format(), UserDefinedFormats::[phonenumber_format()]) -> FormattedNumber::binary(). %% @doc TODO format_by_pattern(_PhoneNumber, _PhoneNumberFormat, _UserDefinedFormats) -> ?NOT_LOADED. -spec format_national_number_with_carrier_code(PhoneNumber::phonenumber(), CarrierCode::binary()) -> FormattedNumber::binary(). %% @doc Formats a phone number in national format for dialing using the carrier as %% specified in the carrier_code. The carrier_code will always be used %% regardless of whether the phone number already has a preferred domestic %% carrier code stored. If carrier_code contains an empty string, return the %% number in national format without any carrier code. format_national_number_with_carrier_code(_PhoneNumber, _CarrierCode) -> ?NOT_LOADED. -spec format_national_number_with_preferred_carrier_code(PhoneNumber::phonenumber(), FallbackCarrierCode::binary()) -> FormattedNumber::binary(). %% @doc Formats a phone number in national format for dialing using the carrier as %% specified in the preferred_domestic_carrier_code field of the PhoneNumber %% object passed in. If that is missing, use the fallback_carrier_code passed %% in instead. If there is no preferred_domestic_carrier_code, and the %% fallback_carrier_code contains an empty string, return the number in %% national format without any carrier code. %% %% Use FormatNationalNumberWithCarrierCode instead if the carrier code passed %% in should take precedence over the number's preferred_domestic_carrier_code %% when formatting. format_national_number_with_preferred_carrier_code(_PhoneNumber, _FallbackCarrierCode) -> ?NOT_LOADED. -spec format_number_for_mobile_dialing(PhoneNumber::phonenumber(), RegionCallingFrom::binary(), WithFormatting::boolean()) -> FormattedNumber::binary(). %% @doc Returns a number formatted in such a way that it can be dialed from a %% mobile phone in a specific region. If the number cannot be reached from %% the region (e.g. some countries block toll-free numbers from being called %% outside of the country), the method returns an empty string. format_number_for_mobile_dialing(_PhoneNumber, _RegionCallingFrom, _WithFormatting) -> ?NOT_LOADED. -spec format_out_of_country_calling_number(PhoneNumber::phonenumber(), CallingFrom::binary()) -> FormattedNumber::binary(). %% @doc Formats a phone number for out-of-country dialing purposes. %% %% Note this function takes care of the case for calling inside of NANPA %% and between Russia and Kazakhstan (who share the same country calling %% code). In those cases, no international prefix is used. For regions which %% have multiple international prefixes, the number in its INTERNATIONAL %% format will be returned instead. format_out_of_country_calling_number(_PhoneNumber, _CallingFrom) -> ?NOT_LOADED. -spec format_in_original_format(PhoneNumber::phonenumber(), RegionCallingFrom::binary()) -> FormattedNumber::binary(). %% @doc Formats a phone number using the original phone number format that the %% number is parsed from. The original format is embedded in the %% country_code_source field of the PhoneNumber object passed in. If such %% information is missing, the number will be formatted into the NATIONAL %% format by default. When the number is an invalid number, the method returns %% the raw input when it is available. format_in_original_format(_PhoneNumber, _RegionCallingFrom) -> ?NOT_LOADED. -spec format_out_of_country_keeping_alpha_chars(PhoneNumber::phonenumber(), CallingFrom::binary()) -> FormattedNumber::binary(). %% @doc Formats a phone number for out-of-country dialing purposes. %% %% Note that in this version, if the number was entered originally using alpha %% characters and this version of the number is stored in raw_input, this %% representation of the number will be used rather than the digit %% representation. Grouping information, as specified by characters such as %% "-" and " ", will be retained. %% %% Caveats: %% 1) This will not produce good results if the country calling code is both %% present in the raw input _and_ is the start of the national number. This %% is not a problem in the regions which typically use alpha numbers. %% 2) This will also not produce good results if the raw input has any %% grouping information within the first three digits of the national number, %% and if the function needs to strip preceding digits/words in the raw input %% before these digits. Normally people group the first three digits together %% so this is not a huge problem - and will be fixed if it proves to be so. format_out_of_country_keeping_alpha_chars(_PhoneNumber, _CallingFrom) -> ?NOT_LOADED. -spec truncate_too_long_number(PhoneNumber::phonenumber()) -> ValidPhoneNumber::phonenumber() | {error, no_valid_number}. %% @doc Attempts to extract a valid number from a phone number that is too long to %% be valid, and resets the PhoneNumber object passed in to that valid %% version. If no valid number could be extracted, the PhoneNumber object %% passed in will not be modified. It returns true if a valid phone number can %% be successfully extracted. truncate_too_long_number(_PhoneNumber) -> ?NOT_LOADED. -spec get_number_type(PhoneNumber::phonenumber()) -> phonenumber_type(). %% @doc Gets the type of a phone number. get_number_type(_PhoneNumber) -> ?NOT_LOADED. -spec is_valid_number(PhoneNumber::phonenumber()) -> boolean(). %% @doc Tests whether a phone number matches a valid pattern. Note this doesn't %% verify the number is actually in use, which is impossible to tell by just %% looking at a number itself. is_valid_number(_PhoneNumber) -> ?NOT_LOADED. -spec is_valid_number_for_region(PhoneNumber::phonenumber(), Region::binary()) -> boolean(). %% @doc Tests whether a phone number is valid for a certain region. Note this %% doesn't verify the number is actually in use, which is impossible to tell %% by just looking at a number itself. If the country calling code is not the %% same as the country calling code for the region, this immediately exits %% with false. After this, the specific number pattern rules for the region %% are examined. %% This is useful for determining for example whether a particular number is %% valid for Canada, rather than just a valid NANPA number. %% Warning: In most cases, you want to use IsValidNumber instead. For %% example, this method will mark numbers from British Crown dependencies %% such as the Isle of Man as invalid for the region "GB" (United Kingdom), %% since it has its own region code, "IM", which may be undesirable. is_valid_number_for_region(_PhoneNumber, _Region) -> ?NOT_LOADED. -spec get_region_code_for_number(PhoneNumber::phonenumber()) -> RegionCode::binary(). %% @doc Returns the region where a phone number is from. This could be used for %% geo-coding at the region level. get_region_code_for_number(_PhoneNumber) -> ?NOT_LOADED. -spec get_country_code_for_region(RegionCode::binary()) -> CountryCode::non_neg_integer(). %% @doc Returns the country calling code for a specific region. For example, %% this would be 1 for the United States, and 64 for New Zealand. get_country_code_for_region(_RegionCode) -> ?NOT_LOADED. -spec get_region_code_for_country_code(CountryCode::non_neg_integer()) -> RegionCode::binary(). %% @doc Returns the region code that matches the specific country code. Note that %% it is possible that several regions share the same country calling code %% (e.g. US and Canada), and in that case, only one of the regions (normally %% the one with the largest population) is returned. If the %% countryCallingCode entered is valid but doesn't match a specific region %% (such as in the case of non-geographical calling codes like 800) the %% RegionCode 001 will be returned (corresponding to the value for World in %% the UN M.49 schema). get_region_code_for_country_code(_CountryCode) -> ?NOT_LOADED. -spec get_region_codes_for_country_calling_code(CountryCallingCode::non_neg_integer()) -> [binary()]. %% @doc Populates a list with the region codes that match the specific country %% calling code. For non-geographical country calling codes, the region code %% 001 is returned. Also, in the case of no region code being found, the list %% is left unchanged. get_region_codes_for_country_calling_code(_CountryCallingCode) -> ?NOT_LOADED. -spec get_region_display_name(RegionCode::binary(), Language::binary()) -> binary(). %% @doc Get display name (country name) for a region code. get_region_display_name(_RegionCode, _Language) -> ?NOT_LOADED. -spec is_nanpa_country(RegionCode::binary()) -> boolean(). %% @doc Checks if this is a region under the North American Numbering Plan %% Administration (NANPA). is_nanpa_country(_RegionCode) -> ?NOT_LOADED. -spec get_ndd_prefix_for_region(RegionCode::binary(), StripNonDigits::boolean()) -> NationalPrefix::binary(). %% @doc Returns the national dialling prefix for a specific region. For example, %% this would be 1 for the United States, and 0 for New Zealand. Set %% strip_non_digits to true to strip symbols like "~" (which indicates a wait %% for a dialling tone) from the prefix returned. If no national prefix is %% present, we return an empty string. get_ndd_prefix_for_region(_RegionCode, _StripNonDigits) -> ?NOT_LOADED. -spec is_possible_number_with_reason(PhoneNumber::phonenumber()) -> ValidationResult::validation_result(). %% @doc Checks whether a phone number is a possible number. It provides a more %% lenient check than IsValidNumber() in the following sense: %% 1. It only checks the length of phone numbers. In particular, it doesn't %% check starting digits of the number. %% 2. It doesn't attempt to figure out the type of the number, but uses %% general rules which applies to all types of phone numbers in a %% region. Therefore, it is much faster than IsValidNumber(). %% 3. For fixed line numbers, many regions have the concept of area code, %% which together with subscriber number constitute the national %% significant number. It is sometimes okay to dial the subscriber %% number only when dialing in the same area. This function will return %% true if the subscriber-number-only version is passed in. On the other %% hand, because IsValidNumber() validates using information on both %% starting digits (for fixed line numbers, that would most likely be %% area codes) and length (obviously includes the length of area codes %% for fixed line numbers), it will return false for the %% subscriber-number-only version. is_possible_number_with_reason(_PhoneNumber) -> ?NOT_LOADED. -spec is_possible_number(PhoneNumber::phonenumber()) -> boolean(). %% @doc Convenience wrapper around IsPossibleNumberWithReason. Instead of returning %% the reason for failure, this method returns a boolean value. is_possible_number(_PhoneNumber) -> ?NOT_LOADED. -spec is_possible_number_for_string(Number::binary(), RegionDialingFrom::binary()) -> boolean(). %% @doc Checks whether a phone number is a possible number given a number in the %% form of a string, and the country where the number could be dialed from. %% It provides a more lenient check than is_valid_number/1. %% See is_possible_number/1 for details. %% %% This method first parses the number, then invokes %% is_possible_number with the resultant PhoneNumber %% object. %% %% region_dialing_from represents the region that we are expecting the number %% to be dialed from. Note this is different from the region where the number %% belongs. For example, the number +1 650 253 0000 is a number that belongs %% to US. When written in this form, it could be dialed from any region. When %% it is written as 00 1 650 253 0000, it could be dialed from any region %% which uses an international dialling prefix of 00. When it is written as %% 650 253 0000, it could only be dialed from within the US, and when written %% as 253 0000, it could only be dialed from within a smaller area in the US %% (Mountain View, CA, to be more specific). %% @see is_possible_number. is_possible_number_for_string(_Number, _RegionDialingFrom) -> ?NOT_LOADED. %% @doc Returns true if the number can be dialled from outside the region, or %% unknown. If the number can only be dialled from within the region, returns %% false. Does not check the number is a valid number. Note that, at the %% moment, this method does not handle short numbers (which are currently all %% presumed to not be diallable from outside their country). can_be_internationally_dialled(_Number) -> ?NOT_LOADED. %% @doc Tests whether a phone number has a geographical association. It checks if %% the number is associated with a certain region in the country to which it %% belongs. Note that this doesn't verify if the number is actually in use. is_number_geographical(_Number) -> ?NOT_LOADED. -spec get_example_number(RegionCode::binary()) -> ValidPhoneNumber::phonenumber() | {error, unknown_region}. %% @doc Gets a valid fixed-line number for the specified region. Returns {error, unknown_region} if %% the region was unknown, or the region 001 is passed in. For 001 %% (representing non-geographical numbers), call %% GetExampleNumberForNonGeoEntity instead. get_example_number(_RegionCode) -> ?NOT_LOADED. -spec get_example_number_for_type(RegionCode::binary(), PhoneNumberType::phonenumber_type()) -> ValidPhoneNumber::phonenumber() | {error, unknown_region}. %% @doc Gets a valid number of the specified type for the specified region. %% Returns false if the region was unknown or 001, or if no example number of %% that type could be found. For 001 (representing non-geographical numbers), %% call GetExampleNumberForNonGeoEntity instead. get_example_number_for_type(_RegionCode, _PhoneNumberType) -> ?NOT_LOADED. -spec get_example_number_for_non_geo_entity(CountryCallingCode::binary()) -> ValidPhoneNumber::phonenumber() | {error, unknown_code}. %% @doc Gets a valid number for the specified country calling code for a %% non-geographical entity. Returns false if the metadata does not contain %% such information, or the country calling code passed in does not belong to %% a non-geographical entity. get_example_number_for_non_geo_entity(_CountryCallingCode) -> ?NOT_LOADED. -spec parse(NumberToParse::binary(), DefaultRegion::binary()) -> PhoneNumber::phonenumber() | {error, term()}. %% @doc Parses a string and returns it in proto buffer format. This method will %% return an error like INVALID_COUNTRY_CODE if the number is not considered %% to be a possible number, and NO_PARSING_ERROR if it parsed correctly. Note %% that validation of whether the number is actually a valid number for a %% particular region is not performed. This can be done separately with %% IsValidNumber(). %% %% number_to_parse can also be provided in RFC3966 format. %% %% default_region represents the country that we are expecting the number to %% be from. This is only used if the number being parsed is not written in %% international format. The country_code for the number in this case would be %% stored as that of the default country supplied. If the number is guaranteed %% to start with a '+' followed by the country calling code, then %% "ZZ" can be supplied. parse(_NumberToParse, _DefaultRegion) -> ?NOT_LOADED. -spec parse_and_keep_raw_input(NumberToParse::binary(), DefaultRegion::binary()) -> PhoneNumber::phonenumber() | {error, term()}. %% @doc Parses a string and returns it in proto buffer format. This method differs %% from parse/2 in that it always populates the raw_input field of the %% protocol buffer with number_to_parse as well as the country_code_source %% field. parse_and_keep_raw_input(_NumberToParse, _DefaultRegion) -> ?NOT_LOADED. -spec is_number_match(FirstNumber::phonenumber(), SecondNumber::phonenumber()) -> match_type(). %% @doc Takes two phone numbers and compares them for equality. %% %% Returns EXACT_MATCH if the country calling code, NSN, presence of a leading %% zero for Italian numbers and any extension present are the same. %% Returns NSN_MATCH if either or both has no country calling code specified, %% and the NSNs and extensions are the same. %% Returns SHORT_NSN_MATCH if either or both has no country calling code %% specified, or the country calling code specified is the same, and one NSN %% could be a shorter version of the other number. This includes the case %% where one has an extension specified, and the other does not. %% Returns NO_MATCH otherwise. %% For example, the numbers +1 345 657 1234 and 657 1234 are a %% SHORT_NSN_MATCH. The numbers +1 345 657 1234 and 345 657 are a NO_MATCH. is_number_match(_FirstNumber, _SecondNumber) -> ?NOT_LOADED. -spec is_number_match_with_two_strings(FirstNumber::binary(), SecondNumber::binary()) -> match_type(). %% @doc Takes two phone numbers as strings and compares them for equality. This %% is a convenience wrapper for IsNumberMatch(PhoneNumber firstNumber, %% PhoneNumber secondNumber). No default region is known. %% Returns INVALID_NUMBER if either number cannot be parsed into a phone %% number. is_number_match_with_two_strings(_FirstNumber, _SecondNumber) -> ?NOT_LOADED. -spec is_number_match_with_one_string(FirstNumber::phonenumber(), SecondNumber::binary()) -> match_type(). %% @doc Takes two phone numbers and compares them for equality. This is a %% convenience wrapper for IsNumberMatch(PhoneNumber firstNumber, %% PhoneNumber secondNumber). No default region is known. %% Returns INVALID_NUMBER if second_number cannot be parsed into a phone %% number. is_number_match_with_one_string(_FirstNumber, _SecondNumber) -> ?NOT_LOADED.