Module: I18nPhoneNumbers::Util
- Defined in:
- lib/i18n_phone_numbers/util.rb
Constant Summary collapse
- PLUS_SIGN =
TODO ? : support extensions ? KNOWN_EXTN_PATTERNS_ = …
'+'
- STAR_SIGN =
'*'
- PLUS_CHARS_ =
'+'
- PLUS_CHARS_PATTERN =
Regexp.compile('[' + PLUS_CHARS_ + ']+')
- LEADING_PLUS_CHARS_PATTERN_ =
Regexp.compile('\A[' + PLUS_CHARS_ + ']+')
- VALID_DIGITS_ =
'0-9'
- VALID_ALPHA_ =
we don’t support it yet
''
- VALID_PUNCTUATION =
add “-escapes” for [ and ] (will be further included in a [] group in another regexp)
'-x ().\\/\\[\\]~'
- KNOWN_EXTN_PATTERNS_ =
we don’t support extensions yet
''
- VALID_START_CHAR_PATTERN =
Regexp.compile('[' + PLUS_CHARS_ + VALID_DIGITS_ + ']')
- SECOND_NUMBER_START_PATTERN_ =
/[\\\/] *x/
- UNWANTED_END_CHAR_PATTERN_ =
Regexp.compile('[^' + VALID_DIGITS_ + VALID_ALPHA_ + '#]+\z')
- CAPTURING_DIGIT_PATTERN =
/([0-9])/
- FIRST_GROUP_PATTERN_ =
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.
/(\\\d)/
- NP_PATTERN_ =
/\$NP/
- FG_PATTERN_ =
/\$FG/
- CC_PATTERN_ =
/\$CC/
- REGION_CODE_FOR_NON_GEO_ENTITY =
'001'
- VALID_PHONE_NUMBER_ =
Regular expression of viable phone numbers. This is location independent.
-> plus_sign*(([punctuation]|[star])*[digits]){3,}([punctuation]|[star]|[digits]|[alpha])*
'[' + PLUS_CHARS_ + ']*(?:[' + VALID_PUNCTUATION + STAR_SIGN + ']*[' + VALID_DIGITS_ + ']){3,}[' + VALID_PUNCTUATION + STAR_SIGN + VALID_ALPHA_ + VALID_DIGITS_ + ']*'
- VALID_PHONE_NUMBER_PATTERN_ =
Regexp.compile('\A' + VALID_PHONE_NUMBER_ + '(?:' + KNOWN_EXTN_PATTERNS_ + ')?' + '\z', 'i')
- MAX_LENGTH_COUNTRY_CODE_ =
3
- UNKNOWN_REGION_ =
'ZZ'
- MIN_LENGTH_FOR_NSN_ =
The minimum and maximum length of the national significant number.
3
- MAX_LENGTH_FOR_NSN_ =
The ITU says the maximum length should be 15, but we have found longer numbers in Germany.
16
- DIGIT_MAPPINGS =
{ '0' => '0', '1' => '1', '2' => '2', '3' => '3', '4' => '4', '5' => '5', '6' => '6', '7' => '7', '8' => '8', '9' => '9' #, # TODO : wait for ruby 1.9 to use unicode # '\uFF10' => '0', # Fullwidth digit 0 # '\uFF11' => '1', # Fullwidth digit 1 # '\uFF12' => '2', # Fullwidth digit 2 # '\uFF13' => '3', # Fullwidth digit 3 # '\uFF14' => '4', # Fullwidth digit 4 # '\uFF15' => '5', # Fullwidth digit 5 # '\uFF16' => '6', # Fullwidth digit 6 # '\uFF17' => '7', # Fullwidth digit 7 # '\uFF18' => '8', # Fullwidth digit 8 # '\uFF19' => '9', # Fullwidth digit 9 # '\u0660' => '0', # Arabic-indic digit 0 # '\u0661' => '1', # Arabic-indic digit 1 # '\u0662' => '2', # Arabic-indic digit 2 # '\u0663' => '3', # Arabic-indic digit 3 # '\u0664' => '4', # Arabic-indic digit 4 # '\u0665' => '5', # Arabic-indic digit 5 # '\u0666' => '6', # Arabic-indic digit 6 # '\u0667' => '7', # Arabic-indic digit 7 # '\u0668' => '8', # Arabic-indic digit 8 # '\u0669' => '9', # Arabic-indic digit 9 # '\u06F0' => '0', # Eastern-Arabic digit 0 # '\u06F1' => '1', # Eastern-Arabic digit 1 # '\u06F2' => '2', # Eastern-Arabic digit 2 # '\u06F3' => '3', # Eastern-Arabic digit 3 # '\u06F4' => '4', # Eastern-Arabic digit 4 # '\u06F5' => '5', # Eastern-Arabic digit 5 # '\u06F6' => '6', # Eastern-Arabic digit 6 # '\u06F7' => '7', # Eastern-Arabic digit 7 # '\u06F8' => '8', # Eastern-Arabic digit 8 # '\u06F9' => '9' # Eastern-Arabic digit 9 }
Class Method Summary collapse
-
.checkRegionForParsing_(numberToParse, defaultRegion) ⇒ Object
Checks to see that the region code used is valid, or if it is not valid, that the number to parse starts with a + symbol so that we can attempt to infer the region from the number.
- .extractCountryCode(fullNumber, nationalNumber) ⇒ Object
-
.extractPossibleNumber(number) ⇒ string
Attempts to extract a possible number from the string passed in.
-
.format(number, numberFormat) ⇒ string
Formats a phone number in the specified format using default rules.
-
.formatAccordingToFormats_(nationalNumber, availableFormats, numberFormat, opt_carrierCode = nil) ⇒ string
Note that carrierCode is optional - if nil or an empty string, no carrier code replacement will take place.
-
.formatNationalNumber_(number, metadata, numberFormat, opt_carrierCode = nil) ⇒ string
Note in some countries, the national number can be written in two completely different ways depending on whether it forms part of the NATIONAL format or INTERNATIONAL format.
-
.formatNumberByFormat_(countryCode, numberFormat, formattedNationalNumber, formattedExtension) ⇒ string
A helper function that is used by format and formatByPattern.
-
.getCountryCodeForRegion(regionCode) ⇒ number
Returns the country calling code for a specific region.
-
.getExampleNumber(regionCode) ⇒ i18n.phonenumbers.PhoneNumber
Gets a valid number for the specified country.
-
.getExampleNumberForType(regionCode, type) ⇒ i18n.phonenumbers.PhoneNumber
Gets a valid number, if any, for the specified country and number type.
- .getMetadataForRegion(regionCode) ⇒ i18n.phonenumbers.PhoneMetadata
-
.getNationalSignificantNumber(number) ⇒ string
Gets the national significant number of the a phone number.
- .getNumberDescByType_(metadata, type) ⇒ i18n.phonenumbers.PhoneNumberDesc
-
.getNumberType(number) ⇒ i18n.phonenumbers.PhoneNumberType
Gets the type of a phone number.
- .getNumberTypeHelper_(nationalNumber, metadata) ⇒ i18n.phonenumbers.PhoneNumberType
-
.getRegionCodeForCountryCode(countryCode) ⇒ string
Returns the region code that matches the specific country code.
-
.getRegionCodeForNumber(number) ⇒ ?string
Returns the country/region where a phone number is from.
- .getRegionCodeForNumberFromRegionList_(number, regionCodes) ⇒ ?string
-
.isMobile?(number) ⇒ boolean
Gets the type of a phone number.
- .isNumberMatchingDesc_(nationalNumber, numberDesc) ⇒ boolean
-
.isValidNumber(number) ⇒ boolean
Tests whether a phone number matches a valid pattern.
-
.isValidNumberForRegion(number, regionCode) ⇒ boolean
Tests whether a phone number is valid for a certain region.
-
.isValidRegionCode_(regionCode) ⇒ boolean
Helper function to check region code is not unknown or null.
-
.isViablePhoneNumber(number) ⇒ boolean
Checks to see if the string of characters could possibly be a phone number at all.
-
.matchesEntirely_(regex, str) ⇒ boolean
Check whether the entire input sequence can be matched against the regular expression.
-
.maybeExtractCountryCode(number, defaultRegionMetadata, nationalNumber, keepRawInput, phoneNumber) ⇒ number
Tries to extract a country code from a number.
-
.maybeGetFormattedExtension_(number, regionCode) ⇒ string
Gets the formatted extension of a phone number, if the phone number had an extension specified.
-
.maybeStripInternationalPrefixAndNormalize(number, possibleIddPrefix) ⇒ i18n.phonenumbers.PhoneNumber.CountryCodeSource
Strips any international prefix (such as +, 00, 011) present in the number provided, normalizes the resulting number, and indicates if an international prefix was present.
-
.maybeStripNationalPrefixAndCarrierCode(number, metadata, carrierCode) ⇒ string
Strips any national prefix (such as 0, 1) present in the number provided.
-
.normalize(number) ⇒ string
Normalizes a string of characters representing a phone number.
-
.normalizeHelper_(number, normalizationReplacements = DIGIT_MAPPINGS, removeNonMatches = true) ⇒ string
Normalizes a string of characters representing a phone number by replacing all characters found in the accompanying map with the values therein, and stripping all other characters if removeNonMatches is true.
-
.normalizeSB_(number) ⇒ Object
Normalizes a string of characters representing a phone number.
- .parse(numberToParse, alpha2) ⇒ Object
- .parseAndKeepRawInput(numberToParse, alpha2) ⇒ Object
- .parseHelper_(numberToParse, defaultRegion, keepRawInput, checkRegion) ⇒ Object
-
.parsePrefixAsIdd_(iddPattern, number) ⇒ boolean
Strips the IDD from the start of the number if present.
- .testNumberLengthAgainstPattern_(numberPattern, number) ⇒ Object
Class Method Details
.checkRegionForParsing_(numberToParse, defaultRegion) ⇒ Object
Checks to see that the region code used is valid, or if it is not valid, that the number to parse starts with a + symbol so that we can attempt to infer the region from the number. Returns false if it cannot use the region provided and the region cannot be inferred.
232 233 234 235 236 237 |
# File 'lib/i18n_phone_numbers/util.rb', line 232 def checkRegionForParsing_(numberToParse, defaultRegion) return isValidRegionCode_(defaultRegion) || (!numberToParse.nil? && numberToParse.length > 0 && numberToParse.match(LEADING_PLUS_CHARS_PATTERN_)) end |
.extractCountryCode(fullNumber, nationalNumber) ⇒ Object
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 |
# File 'lib/i18n_phone_numbers/util.rb', line 532 def extractCountryCode(fullNumber, nationalNumber) [fullNumber.length, MAX_LENGTH_COUNTRY_CODE_].min.times do |i| potentialCountryCode = fullNumber[0..i].to_i if I18nPhoneNumbers::Metadata.countryCodeToRegionCodeMap.has_key?(potentialCountryCode) nationalNumber.replace(fullNumber[(i+1)..-1]) return potentialCountryCode end end return 0 end |
.extractPossibleNumber(number) ⇒ string
Attempts to extract a possible number from the string passed in. This currently strips all leading characters that could not be used to start a phone number. Characters that can be used to start a phone number are defined in the VALID_START_CHAR_PATTERN. If none of these characters are found in the number passed in, an empty string is returned. This function also attempts to strip off any alternative extensions or endings if two or more are present, such as in the case of: (530) 583-6985 x302/x2303. The second extension here makes this actually two phone numbers, (530) 583-6985 x302 and (530) 583-6985 x2303. We remove the second extension so that the first number is parsed correctly.
867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 |
# File 'lib/i18n_phone_numbers/util.rb', line 867 def extractPossibleNumber(number) start = number.index(VALID_START_CHAR_PATTERN) if !start.nil? possibleNumber = number[start..-1] # Remove trailing non-alpha non-numerical characters. possibleNumber = possibleNumber.gsub(UNWANTED_END_CHAR_PATTERN_, '') # Check for extra numbers at the end. secondNumberStart = possibleNumber.index(SECOND_NUMBER_START_PATTERN_) if !secondNumberStart.nil? possibleNumber = possibleNumber[0..secondNumberStart] end else possibleNumber = '' end return possibleNumber end |
.format(number, numberFormat) ⇒ string
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. Note that if the phone number has a country code of 0 or an otherwise invalid country code, we cannot work out which formatting rules to apply so we return the national significant number with no formatting applied.
1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 |
# File 'lib/i18n_phone_numbers/util.rb', line 1002 def format(number, numberFormat) countryCallingCode = number.country_code || 0 nationalSignificantNumber = getNationalSignificantNumber(number) if (numberFormat == I18nPhoneNumbers::PhoneNumberFormat::E164) # Early exit for E164 case since no formatting of the national number needs # to be applied. Extensions are not formatted. return formatNumberByFormat_(countryCallingCode, I18nPhoneNumbers::PhoneNumberFormat::E164, nationalSignificantNumber, '') end # Note getRegionCodeForCountryCode() is used because formatting information # for countries which share a country code is contained by only one country # for performance reasons. For example, for NANPA countries it will be # contained in the metadata for US. regionCode = getRegionCodeForCountryCode(countryCallingCode) return nationalSignificantNumber if !isValidRegionCode_(regionCode) = getMetadataForRegion(regionCode) formattedExtension = maybeGetFormattedExtension_(number, regionCode) formattedNationalNumber = formatNationalNumber_(nationalSignificantNumber, , numberFormat) return formatNumberByFormat_(countryCallingCode, numberFormat, formattedNationalNumber, formattedExtension) end |
.formatAccordingToFormats_(nationalNumber, availableFormats, numberFormat, opt_carrierCode = nil) ⇒ string
Note that carrierCode is optional - if nil or an empty string, no carrier code replacement will take place.
1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 |
# File 'lib/i18n_phone_numbers/util.rb', line 1124 def formatAccordingToFormats_(nationalNumber, availableFormats, numberFormat, opt_carrierCode = nil) availableFormats.each do |numFormat| ldp = numFormat.leading_digits_patterns # We always use the last leading_digits_pattern, as it is the most detailed. if ldp.empty? || nationalNumber.index(Regexp.compile(ldp[-1])) == 0 patternToMatch = Regexp.new(numFormat.pattern) if matchesEntirely_(patternToMatch, nationalNumber) numberFormatRule = numFormat.format domesticCarrierCodeFormattingRule = numFormat.carrier_code_formatting_rule if (numberFormat == I18nPhoneNumbers::PhoneNumberFormat::NATIONAL && !opt_carrierCode.blank? && !domesticCarrierCodeFormattingRule.blank?) # Replace the $CC in the formatting rule with the desired carrier code. carrierCodeFormattingRule = domesticCarrierCodeFormattingRule.sub(CC_PATTERN_, opt_carrierCode) # Now replace the $FG in the formatting rule with the first group # and the carrier code combined in the appropriate way. numberFormatRule = numberFormatRule.sub(FIRST_GROUP_PATTERN_, carrierCodeFormattingRule) return nationalNumber.sub(patternToMatch, numberFormatRule) else # Use the national prefix formatting rule instead. nationalPrefixFormattingRule = numFormat.national_prefix_formatting_rule if (numberFormat == I18nPhoneNumbers::PhoneNumberFormat::NATIONAL && !nationalPrefixFormattingRule.blank?) return nationalNumber.sub(patternToMatch, numberFormatRule.sub(FIRST_GROUP_PATTERN_, nationalPrefixFormattingRule)) else return nationalNumber.sub(patternToMatch, numberFormatRule) end end end end end # If no pattern above is matched, we format the number as a whole. return nationalNumber end |
.formatNationalNumber_(number, metadata, numberFormat, opt_carrierCode = nil) ⇒ string
Note in some countries, the national number can be written in two completely different ways depending on whether it forms part of the NATIONAL format or INTERNATIONAL format. The numberFormat parameter here is used to specify which format to use for those cases. If a carrierCode is specified, this will be inserted into the formatted string to replace $CC.
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 |
# File 'lib/i18n_phone_numbers/util.rb', line 1096 def formatNationalNumber_(number, , numberFormat, opt_carrierCode = nil) intlNumberFormats = .intl_number_formats # When the intlNumberFormats exists, we use that to format national number # for the INTERNATIONAL format instead of using the numberDesc.numberFormats. if (intlNumberFormats.length == 0 || numberFormat == I18nPhoneNumbers::PhoneNumberFormat::NATIONAL) availableFormats = .number_formats else availableFormats = .intl_number_formats end return formatAccordingToFormats_(number, availableFormats, numberFormat, opt_carrierCode) end |
.formatNumberByFormat_(countryCode, numberFormat, formattedNationalNumber, formattedExtension) ⇒ string
A helper function that is used by format and formatByPattern.
1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 |
# File 'lib/i18n_phone_numbers/util.rb', line 1044 def formatNumberByFormat_(countryCode, numberFormat, formattedNationalNumber, formattedExtension) pnf = I18nPhoneNumbers::PhoneNumberFormat res = case numberFormat when pnf::E164 then PLUS_SIGN + countryCode.to_s + formattedNationalNumber + formattedExtension when pnf::INTERNATIONAL then PLUS_SIGN + countryCode.to_s + ' ' + formattedNationalNumber + formattedExtension # "else" is including pnf::NATIONAL else formattedNationalNumber + formattedExtension end return res end |
.getCountryCodeForRegion(regionCode) ⇒ number
Returns the country calling code for a specific region. For example, this would be 1 for the United States, and 64 for New Zealand.
975 976 977 978 979 980 981 982 983 |
# File 'lib/i18n_phone_numbers/util.rb', line 975 def getCountryCodeForRegion(regionCode) return 0 if !isValidRegionCode_(regionCode) = getMetadataForRegion(regionCode) return 0 if == nil return .country_code || 0 end |
.getExampleNumber(regionCode) ⇒ i18n.phonenumbers.PhoneNumber
Gets a valid number for the specified country.
1182 1183 1184 |
# File 'lib/i18n_phone_numbers/util.rb', line 1182 def getExampleNumber(regionCode) return getExampleNumberForType(regionCode, I18nPhoneNumbers::PhoneNumberType::FIXED_LINE) end |
.getExampleNumberForType(regionCode, type) ⇒ i18n.phonenumbers.PhoneNumber
Gets a valid number, if any, for the specified country and number type.
1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 |
# File 'lib/i18n_phone_numbers/util.rb', line 1196 def getExampleNumberForType(regionCode, type) desc = getNumberDescByType_(getMetadataForRegion(regionCode), type) begin if !desc.example_number.blank? return parse(desc.example_number, regionCode) end rescue end return nil end |
.getMetadataForRegion(regionCode) ⇒ i18n.phonenumbers.PhoneMetadata
637 638 639 640 641 |
# File 'lib/i18n_phone_numbers/util.rb', line 637 def getMetadataForRegion(regionCode) return regionCode.nil? ? nil : I18nPhoneNumbers::Metadata::countryToMetadata[regionCode.upcase] end |
.getNationalSignificantNumber(number) ⇒ string
Gets the national significant number of the a phone number. Note a national significant number doesn’t contain a national prefix or any formatting.
836 837 838 839 840 841 842 843 844 845 846 847 848 |
# File 'lib/i18n_phone_numbers/util.rb', line 836 def getNationalSignificantNumber(number) # If a leading zero has been set, we prefix this now. Note this is not a # national prefix. nationalNumber = number.national_number.to_s if number.italian_leading_zero return '0' + nationalNumber end return nationalNumber end |
.getNumberDescByType_(metadata, type) ⇒ i18n.phonenumbers.PhoneNumberDesc
1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 |
# File 'lib/i18n_phone_numbers/util.rb', line 1216 def getNumberDescByType_(, type) case type # when I18nPhoneNumbers::PhoneNumberType::PREMIUM_RATE then metadata.premium_rate # when I18nPhoneNumbers::PhoneNumberType::TOLL_FREE then metadata.toll_free when I18nPhoneNumbers::PhoneNumberType::MOBILE then .mobile when I18nPhoneNumbers::PhoneNumberType::FIXED_LINE, I18nPhoneNumbers::PhoneNumberType::FIXED_LINE_OR_MOBILE then .fixed_line # when I18nPhoneNumbers::PhoneNumberType::SHARED_COST then metadata.shared_cost # when I18nPhoneNumbers::PhoneNumberType::VOIP then metadata.voip # when I18nPhoneNumbers::PhoneNumberType::PERSONAL_NUMBER then metadata.personal_number # when I18nPhoneNumbers::PhoneNumberType::PAGER then metadata.pager # when I18nPhoneNumbers::PhoneNumberType::UAN then metadata.uan else .general_desc end end |
.getNumberType(number) ⇒ i18n.phonenumbers.PhoneNumberType
Gets the type of a phone number.
649 650 651 652 653 654 655 656 657 658 659 660 661 |
# File 'lib/i18n_phone_numbers/util.rb', line 649 def getNumberType(number) regionCode = getRegionCodeForNumber(number) if !isValidRegionCode_(regionCode) && REGION_CODE_FOR_NON_GEO_ENTITY != regionCode return I18nPhoneNumbers::PhoneNumberType::UNKNOWN end nationalSignificantNumber = getNationalSignificantNumber(number) return getNumberTypeHelper_(nationalSignificantNumber, getMetadataForRegion(regionCode)) end |
.getNumberTypeHelper_(nationalNumber, metadata) ⇒ i18n.phonenumbers.PhoneNumberType
735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 |
# File 'lib/i18n_phone_numbers/util.rb', line 735 def getNumberTypeHelper_(nationalNumber, ) generalNumberDesc = .general_desc if generalNumberDesc.national_number_pattern.nil? || !isNumberMatchingDesc_(nationalNumber, generalNumberDesc) return I18nPhoneNumbers::PhoneNumberType::UNKNOWN end # TODO : init and use types different from MOBILE and FIXED # if isNumberMatchingDesc_(nationalNumber, metadata.premium_rate) # return I18nPhoneNumbers::PhoneNumberType::PREMIUM_RATE # end # # if isNumberMatchingDesc_(nationalNumber, metadata.toll_free) # return I18nPhoneNumbers::PhoneNumberType::TOLL_FREE # end # # if isNumberMatchingDesc_(nationalNumber, metadata.shared_cost) # return I18nPhoneNumbers::PhoneNumberType::SHARED_COST # end # # if isNumberMatchingDesc_(nationalNumber, metadata.voip) # return I18nPhoneNumbers::PhoneNumberType::VOIP # end # # if isNumberMatchingDesc_(nationalNumber, metadata.personal_number) # return I18nPhoneNumbers::PhoneNumberType::PERSONAL_NUMBER # end # # if isNumberMatchingDesc_(nationalNumber, metadata.pager) # return I18nPhoneNumbers::PhoneNumberType::PAGER # end # # if isNumberMatchingDesc_(nationalNumber, metadata.uan) # return I18nPhoneNumbers::PhoneNumberType::UAN # end # # if isNumberMatchingDesc_(nationalNumber, metadata.voicemail) # return I18nPhoneNumbers::PhoneNumberType::VOICEMAIL # end isFixedLine = isNumberMatchingDesc_(nationalNumber, .fixed_line) if isFixedLine if .same_mobile_and_fixed_line_pattern return I18nPhoneNumbers::PhoneNumberType::FIXED_LINE_OR_MOBILE elsif isNumberMatchingDesc_(nationalNumber, .mobile) return I18nPhoneNumbers::PhoneNumberType::FIXED_LINE_OR_MOBILE end return I18nPhoneNumbers::PhoneNumberType::FIXED_LINE end # Otherwise, test to see if the number is mobile. Only do this if certain # that the patterns for mobile and fixed line aren't the same. if !.same_mobile_and_fixed_line_pattern && isNumberMatchingDesc_(nationalNumber, .mobile) return I18nPhoneNumbers::PhoneNumberType::MOBILE end return I18nPhoneNumbers::PhoneNumberType::UNKNOWN end |
.getRegionCodeForCountryCode(countryCode) ⇒ string
Returns the region code that matches the specific country code. In the case of no region code being found, ZZ will be returned. In the case of multiple regions, the one designated in the metadata as the “main” country for this calling code will be returned.
626 627 628 629 630 631 632 |
# File 'lib/i18n_phone_numbers/util.rb', line 626 def getRegionCodeForCountryCode(countryCode) regionCodes = I18nPhoneNumbers::Metadata::countryCodeToRegionCodeMap[countryCode] return regionCodes.nil? ? UNKNOWN_REGION_ : regionCodes[0] end |
.getRegionCodeForNumber(number) ⇒ ?string
Returns the country/region where a phone number is from. This could be used for geo-coding in the country/region level.
682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 |
# File 'lib/i18n_phone_numbers/util.rb', line 682 def getRegionCodeForNumber(number) return nil if number.nil? countryCode = number.country_code || 0 regions = I18nPhoneNumbers::Metadata::countryCodeToRegionCodeMap[countryCode] return nil if regions.nil? if regions.length == 1 return regions[0] else return getRegionCodeForNumberFromRegionList_(number, regions) end end |
.getRegionCodeForNumberFromRegionList_(number, regionCodes) ⇒ ?string
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 |
# File 'lib/i18n_phone_numbers/util.rb', line 705 def getRegionCodeForNumberFromRegionList_(number, regionCodes) nationalNumber = number.national_number.to_s regionCodes.each { |regionCode| # If leadingDigits is present, use this. Otherwise, do full validation. = getMetadataForRegion(regionCode) if !.leading_digits.blank? return regionCode if !!nationalNumber.match(Regexp.compile('\A' + .leading_digits)) # starts with leading_digits pattern elsif getNumberTypeHelper_(nationalNumber, ) != I18nPhoneNumbers::PhoneNumberType::UNKNOWN return regionCode end } return nil end |
.isMobile?(number) ⇒ boolean
Gets the type of a phone number.
669 670 671 672 |
# File 'lib/i18n_phone_numbers/util.rb', line 669 def isMobile?(number) return [I18nPhoneNumbers::PhoneNumberType::MOBILE, I18nPhoneNumbers::PhoneNumberType::FIXED_LINE_OR_MOBILE].include?(getNumberType(number)) end |
.isNumberMatchingDesc_(nationalNumber, numberDesc) ⇒ boolean
806 807 808 809 810 811 |
# File 'lib/i18n_phone_numbers/util.rb', line 806 def isNumberMatchingDesc_(nationalNumber, numberDesc) return matchesEntirely_(numberDesc.possible_number_pattern, nationalNumber) && matchesEntirely_(numberDesc.national_number_pattern, nationalNumber) end |
.isValidNumber(number) ⇒ boolean
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.
921 922 923 924 925 926 |
# File 'lib/i18n_phone_numbers/util.rb', line 921 def isValidNumber(number) regionCode = getRegionCodeForNumber(number) return isValidRegionCode_(regionCode) && isValidNumberForRegion(number, regionCode) end |
.isValidNumberForRegion(number, regionCode) ⇒ boolean
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 code is not the same as the country 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.
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 |
# File 'lib/i18n_phone_numbers/util.rb', line 943 def isValidNumberForRegion(number, regionCode) return false if (number.country_code || 0) != getCountryCodeForRegion(regionCode) = getMetadataForRegion(regionCode) generalNumDesc = .general_desc nationalSignificantNumber = getNationalSignificantNumber(number) # For countries where we don't have metadata for PhoneNumberDesc, we treat # any number passed in as a valid number if its national significant number # is between the minimum and maximum lengths defined by ITU for a national # significant number. if generalNumDesc.national_number_pattern.blank? numberLength = nationalSignificantNumber.length return numberLength > MIN_LENGTH_FOR_NSN_ && numberLength <= MAX_LENGTH_FOR_NSN_ end return getNumberTypeHelper_(nationalSignificantNumber, ) != I18nPhoneNumbers::PhoneNumberType::UNKNOWN end |
.isValidRegionCode_(regionCode) ⇒ boolean
Helper function to check region code is not unknown or null.
820 821 822 823 824 825 826 |
# File 'lib/i18n_phone_numbers/util.rb', line 820 def isValidRegionCode_(regionCode) return !regionCode.nil? && regionCode != REGION_CODE_FOR_NON_GEO_ENTITY && I18nPhoneNumbers::Metadata::countryToMetadata.has_key?(regionCode.upcase) end |
.isViablePhoneNumber(number) ⇒ boolean
Checks to see if the string of characters could possibly be a phone number at all. At the moment, checks to see that the string begins with at least 3 digits, ignoring any punctuation commonly found in phone numbers. This method does not require the number to be normalized in advance - but does assume that leading non-number symbols have been removed, such as by the method extractPossibleNumber.
904 905 906 907 908 909 910 |
# File 'lib/i18n_phone_numbers/util.rb', line 904 def isViablePhoneNumber(number) return false if (number.length < MIN_LENGTH_FOR_NSN_) return matchesEntirely_(VALID_PHONE_NUMBER_PATTERN_, number) end |
.matchesEntirely_(regex, str) ⇒ boolean
Check whether the entire input sequence can be matched against the regular expression.
523 524 525 526 527 528 529 |
# File 'lib/i18n_phone_numbers/util.rb', line 523 def matchesEntirely_(regex, str) matchedGroups = str.match(regex) # regex is a string, match will correctly convert it into a regex first return !!(matchedGroups && matchedGroups[0].length == str.length) end |
.maybeExtractCountryCode(number, defaultRegionMetadata, nationalNumber, keepRawInput, phoneNumber) ⇒ number
Tries to extract a country code from a number. This method will return zero if no country code is considered to be present. Country codes are extracted in the following ways:
- by stripping the international dialing prefix of the country the person is
dialing from, if this is present in the number, and looking at the next digits
- by stripping the '+' sign if present and then looking at the next digits
- by comparing the start of the number and the country code of the default
region. If the number is not considered possible for the numbering plan of the default region initially, but starts with the country code of this region, validation will be reattempted after stripping this country code. If this number is considered a possible number, then the first digits will be considered the country code and removed as such.
It will throw a i18n.phonenumbers.Error if the number starts with a ‘+’ but the country code supplied after this does not match that of any known country.
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/i18n_phone_numbers/util.rb', line 275 def maybeExtractCountryCode(number, defaultRegionMetadata, nationalNumber, keepRawInput, phoneNumber) if (number.length == 0) return 0 end fullNumber = number.dup possibleCountryIddPrefix = nil if !defaultRegionMetadata.nil? possibleCountryIddPrefix = defaultRegionMetadata.international_prefix end if possibleCountryIddPrefix.nil? possibleCountryIddPrefix = 'NonMatch' # put something that will NEVER match end countryCodeSource = maybeStripInternationalPrefixAndNormalize(fullNumber, possibleCountryIddPrefix) if keepRawInput phoneNumber.country_code_source = countryCodeSource end if countryCodeSource != I18nPhoneNumbers::CountryCodeSource::FROM_DEFAULT_COUNTRY if fullNumber.length < MIN_LENGTH_FOR_NSN_ raise "TOO_SHORT_AFTER_IDD" end potentialCountryCode = extractCountryCode(fullNumber, nationalNumber) if potentialCountryCode != 0 phoneNumber.country_code = potentialCountryCode return potentialCountryCode end # If this fails, they must be using a strange country code that we don't # recognize, or that doesn't exist. raise "INVALID_COUNTRY_CODE" elsif !defaultRegionMetadata.nil? # Check to see if the number starts with the country calling code for the # default region. If so, we remove the country calling code, and do some # checks on the validity of the number before and after. defaultCountryCode = defaultRegionMetadata.country_code defaultCountryCodeString = defaultCountryCode.to_s normalizedNumber = fullNumber.dup if normalizedNumber.match(Regexp.new('\A' + defaultCountryCodeString)) potentialNationalNumber = normalizedNumber[(defaultCountryCodeString.length)..-1] generalDesc = defaultRegionMetadata.general_desc validNumberPattern = Regexp.new(generalDesc.national_number_pattern) maybeStripNationalPrefixAndCarrierCode(potentialNationalNumber, defaultRegionMetadata, nil) potentialNationalNumberStr = potentialNationalNumber.dup possibleNumberPattern = generalDesc.possible_number_pattern # If the number was not valid before but is valid now, or if it was too # long before, we consider the number with the country calling code # stripped to be a better result and keep that instead. if (!matchesEntirely_(validNumberPattern, fullNumber) && matchesEntirely_(validNumberPattern, potentialNationalNumberStr)) || (testNumberLengthAgainstPattern_(possibleNumberPattern, fullNumber) == "TOO_LONG") nationalNumber = nationalNumber.replace(potentialNationalNumberStr) if keepRawInput phoneNumber.country_code_source = I18nPhoneNumbers::CountryCodeSource::FROM_NUMBER_WITHOUT_PLUS_SIGN end phoneNumber.country_code = defaultCountryCode return defaultCountryCode end end end # No country code present. phoneNumber.country_code = 0 return 0 end |
.maybeGetFormattedExtension_(number, regionCode) ⇒ string
Gets the formatted extension of a phone number, if the phone number had an extension specified. If not, it returns an empty string.
1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 |
# File 'lib/i18n_phone_numbers/util.rb', line 1068 def maybeGetFormattedExtension_(number, regionCode) # NOT SUPPORTED YET return '' # if number.extension.blank? # return '' # else # return formatExtension_(number.getExtensionOrDefault(), regionCode) # end end |
.maybeStripInternationalPrefixAndNormalize(number, possibleIddPrefix) ⇒ i18n.phonenumbers.PhoneNumber.CountryCodeSource
Strips any international prefix (such as +, 00, 011) present in the number provided, normalizes the resulting number, and indicates if an international prefix was present.
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 |
# File 'lib/i18n_phone_numbers/util.rb', line 393 def maybeStripInternationalPrefixAndNormalize(number, possibleIddPrefix) numberStr = number.dup if numberStr.length == 0 return I18nPhoneNumbers::CountryCodeSource::FROM_DEFAULT_COUNTRY end # Check to see if the number begins with one or more plus signs. if numberStr.match(LEADING_PLUS_CHARS_PATTERN_) numberStr = numberStr.sub(LEADING_PLUS_CHARS_PATTERN_, '') # strip leading + # Can now normalize the rest of the number since we've consumed the "+" # sign at the start. number.replace(normalize(numberStr)) return I18nPhoneNumbers::CountryCodeSource::FROM_NUMBER_WITH_PLUS_SIGN end # Attempt to parse the first digits as an international prefix. iddPattern = Regexp.new(possibleIddPrefix) normalizeSB_(number) return parsePrefixAsIdd_(iddPattern, number) ? I18nPhoneNumbers::CountryCodeSource::FROM_NUMBER_WITH_IDD : I18nPhoneNumbers::CountryCodeSource::FROM_DEFAULT_COUNTRY end |
.maybeStripNationalPrefixAndCarrierCode(number, metadata, carrierCode) ⇒ string
Strips any national prefix (such as 0, 1) present in the number provided.
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 |
# File 'lib/i18n_phone_numbers/util.rb', line 558 def maybeStripNationalPrefixAndCarrierCode(number, , carrierCode) numberStr = number.dup possibleNationalPrefix = .national_prefix_for_parsing if numberStr == '' || possibleNationalPrefix.blank? # Early return for numbers of zero length. return false end # Attempt to parse the first digits as a national prefix. prefixPattern = Regexp.new('\A(?:' + possibleNationalPrefix + ')') prefixMatcher = numberStr.match(prefixPattern) # nil or MatchData instance (same behavior as Array) if (prefixMatcher) nationalNumberRule = Regexp.new(.general_desc.national_number_pattern) # prefixMatcher[numOfGroups] == nil implies nothing was captured by the # capturing groups in possibleNationalPrefix; therefore, no transformation # is necessary, and we just remove the national prefix. numOfGroups = prefixMatcher.length - 1 transformRule = .national_prefix_transform_rule noTransform = transformRule.blank? || prefixMatcher[numOfGroups].nil? || prefixMatcher[numOfGroups].length == 0 # Juste remove the national prefix if noTransform transformedNumber = numberStr[(prefixMatcher[0].length)..-1] # Apply the transformRule else transformedNumber = numberStr.sub(prefixPattern, transformRule) end # If the original number was viable, and the resultant number is not, # we return. if matchesEntirely_(nationalNumberRule, numberStr) && !matchesEntirely_(nationalNumberRule, transformedNumber) return false end if (noTransform && numOfGroups > 0 && !prefixMatcher[1].nil?) || (!noTransform && numOfGroups > 1) if !carrierCode.nil? carrierCode += prefixMatcher[1] end end number.replace(transformedNumber) return true end return false end |
.normalize(number) ⇒ string
Normalizes a string of characters representing a phone number. This performs the following conversions:
- Wide-ascii digits are converted to normal ASCII (European) digits.
- Punctuation is stripped.
- Arabic-Indic numerals are converted to European numerals.
479 480 481 |
# File 'lib/i18n_phone_numbers/util.rb', line 479 def normalize(number) return normalizeHelper_(number, DIGIT_MAPPINGS, true) end |
.normalizeHelper_(number, normalizationReplacements = DIGIT_MAPPINGS, removeNonMatches = true) ⇒ string
Normalizes a string of characters representing a phone number by replacing all characters found in the accompanying map with the values therein, and stripping all other characters if removeNonMatches is true.
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 |
# File 'lib/i18n_phone_numbers/util.rb', line 496 def normalizeHelper_(number, normalizationReplacements = DIGIT_MAPPINGS, removeNonMatches = true) normalizedNumber = '' number.each_char do |character| newDigit = normalizationReplacements[character.upcase()] if !newDigit.nil? normalizedNumber << newDigit elsif !removeNonMatches normalizedNumber << character end # If neither of the above are true, we remove this character. end return normalizedNumber end |
.normalizeSB_(number) ⇒ Object
Normalizes a string of characters representing a phone number. This is a wrapper for normalize(String number) but does in-place normalization of the StringBuffer provided.
462 463 464 465 466 467 |
# File 'lib/i18n_phone_numbers/util.rb', line 462 def normalizeSB_(number) normalizedNumber = normalize(number) number.replace(normalizedNumber) end |
.parse(numberToParse, alpha2) ⇒ Object
110 111 112 113 114 |
# File 'lib/i18n_phone_numbers/util.rb', line 110 def parse(numberToParse, alpha2) return self.parseHelper_(numberToParse, alpha2.upcase, false, true) end |
.parseAndKeepRawInput(numberToParse, alpha2) ⇒ Object
116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/i18n_phone_numbers/util.rb', line 116 def parseAndKeepRawInput(numberToParse, alpha2) if !isValidRegionCode_(alpha2) if (numberToParse.length > 0 && numberToParse[0..0] != PLUS_SIGN) raise "INVALID_COUNTRY_CODE" end end return self.parseHelper_(numberToParse, alpha2.upcase, true, true) end |
.parseHelper_(numberToParse, defaultRegion, keepRawInput, checkRegion) ⇒ Object
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/i18n_phone_numbers/util.rb', line 129 def parseHelper_(numberToParse, defaultRegion, keepRawInput, checkRegion) if numberToParse.blank? raise "NOT_A_NUMBER" end nationalNumber = extractPossibleNumber(numberToParse) if !isViablePhoneNumber(nationalNumber) raise "NOT_A_NUMBER" end if checkRegion && !checkRegionForParsing_(nationalNumber, defaultRegion) raise "INVALID_COUNTRY_CODE" end # build PhoneNumber instance phoneNumber = I18nPhoneNumbers::PhoneNumber.new if keepRawInput phoneNumber.raw_input = numberToParse end # Attempt to parse extension first, since it doesn't require # country-specific data and we want to have the non-normalised number here. # # phoneNumber.extension = self.maybeStripExtension(nationalNumber) regionMetadata = I18nPhoneNumbers::Metadata.countryToMetadata[defaultRegion] # Check to see if the number is given in international format so we know # whether this number is from the default country or not. normalizedNationalNumber = '' countryCode = maybeExtractCountryCode(nationalNumber, regionMetadata, normalizedNationalNumber, keepRawInput, phoneNumber) if countryCode != 0 phoneNumberRegion = getRegionCodeForCountryCode(countryCode) if phoneNumberRegion != defaultRegion regionMetadata = getMetadataForRegion(phoneNumberRegion) end else # If no extracted country code, use the region supplied instead. The # national number is just the normalized version of the number we were # given to parse normalizeSB_(nationalNumber) normalizedNationalNumber.replace(nationalNumber.dup) if !defaultRegion.nil? countryCode = regionMetadata.country_code phoneNumber.country_code = countryCode elsif keepRawInput phoneNumber.country_code_source = nil end end if normalizedNationalNumber.length < MIN_LENGTH_FOR_NSN_ raise "TOO_SHORT_NSN" end if !regionMetadata.nil? carrierCode = '' maybeStripNationalPrefixAndCarrierCode(normalizedNationalNumber, regionMetadata, carrierCode) if keepRawInput phoneNumber.preferred_domestic_carrier_code = carrierCode end end normalizedNationalNumberStr = normalizedNationalNumber.dup lengthOfNationalNumber = normalizedNationalNumberStr.length raise "TOO_SHORT_NSN" if lengthOfNationalNumber < MIN_LENGTH_FOR_NSN_ raise "TOO_LONG" if lengthOfNationalNumber > MAX_LENGTH_FOR_NSN_ if normalizedNationalNumberStr[0..0] == '0' && regionMetadata.leading_zero_possible phoneNumber.italian_leading_zero = true end phoneNumber.national_number = normalizedNationalNumberStr.to_i # to_i, really ? return phoneNumber end |
.parsePrefixAsIdd_(iddPattern, number) ⇒ boolean
Strips the IDD from the start of the number if present. Helper function used by maybeStripInternationalPrefixAndNormalize.
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 |
# File 'lib/i18n_phone_numbers/util.rb', line 434 def parsePrefixAsIdd_(iddPattern, number) if number.index(iddPattern) == 0 matchEnd = number.match(iddPattern)[0].length matchedGroups = number[matchEnd..-1].match(CAPTURING_DIGIT_PATTERN) if matchedGroups && matchedGroups[1] == '0' return false end number.replace(number[matchEnd..-1]) return true end return false end |
.testNumberLengthAgainstPattern_(numberPattern, number) ⇒ Object
370 371 372 373 374 375 376 377 378 |
# File 'lib/i18n_phone_numbers/util.rb', line 370 def testNumberLengthAgainstPattern_(numberPattern, number) if matchesEntirely_(numberPattern, number) return "IS_POSSIBLE" end return number.match('\A' + numberPattern) ? "TOO_LONG" : "TOO_SHORT" end |