Skip to content

Commit

Permalink
Merge pull request #149 from hemachandran-kalaimani/issue-146-add-met…
Browse files Browse the repository at this point in the history
…hod-getSafeDisplayName

Implement PHONE_NUMBER_CARRIER.getSafeDisplayName of the JAVA version
  • Loading branch information
rowanseymour authored Nov 17, 2023
2 parents 6096ac3 + 325662c commit bc8db54
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 79 deletions.
175 changes: 96 additions & 79 deletions phonenumbers.go
Original file line number Diff line number Diff line change
Expand Up @@ -741,17 +741,23 @@ func isViablePhoneNumber(number string) bool {

// Normalizes a string of characters representing a phone number. This
// performs the following conversions:
//
// - Punctuation is stripped.
//
// - For ALPHA/VANITY numbers:
// - Letters are converted to their numeric representation on a telephone
// keypad. The keypad used here is the one defined in ITU Recommendation
// E.161. This is only done if there are 3 or more letters in the
// number, to lessen the risk that such letters are typos.
//
// - Letters are converted to their numeric representation on a telephone
// keypad. The keypad used here is the one defined in ITU Recommendation
// E.161. This is only done if there are 3 or more letters in the
// number, to lessen the risk that such letters are typos.
//
// - For other numbers:
// - Wide-ascii digits are converted to normal ASCII (European) digits.
// - Arabic-Indic numerals are converted to European numerals.
// - Spurious alpha characters are stripped.
//
// - Wide-ascii digits are converted to normal ASCII (European) digits.
//
// - Arabic-Indic numerals are converted to European numerals.
//
// - Spurious alpha characters are stripped.
func normalize(number string) string {
if VALID_ALPHA_PHONE_PATTERN.MatchString(number) {
return normalizeHelper(number, ALPHA_PHONE_MAPPINGS, true)
Expand Down Expand Up @@ -848,33 +854,33 @@ func ConvertAlphaCharactersInNumber(number string) string {
// 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:
//
// number, err := Parse("16502530000", "US");
// // ... deal with err appropriately ...
// nationalSignificantNumber := GetNationalSignificantNumber(number);
// var areaCode, subscriberNumber;
// number, err := Parse("16502530000", "US");
// // ... deal with err appropriately ...
// nationalSignificantNumber := GetNationalSignificantNumber(number);
// var areaCode, subscriberNumber;
//
// int areaCodeLength = GetLengthOfGeographicalAreaCode(number);
// if (areaCodeLength > 0) {
// areaCode = nationalSignificantNumber[0:areaCodeLength];
// subscriberNumber = nationalSignificantNumber[areaCodeLength:];
// } else {
// areaCode = "";
// subscriberNumber = nationalSignificantNumber;
// }
// int areaCodeLength = GetLengthOfGeographicalAreaCode(number);
// if (areaCodeLength > 0) {
// areaCode = nationalSignificantNumber[0:areaCodeLength];
// subscriberNumber = nationalSignificantNumber[areaCodeLength:];
// } else {
// areaCode = "";
// subscriberNumber = nationalSignificantNumber;
// }
//
// N.B.: area code is a very ambiguous concept, so the I18N team generally
// recommends against using it for most purposes, but recommends 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.
// - 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.
func GetLengthOfGeographicalAreaCode(number *PhoneNumber) int {
metadata := getMetadataForRegion(GetRegionCodeForNumber(number))
if metadata == nil {
Expand Down Expand Up @@ -903,23 +909,23 @@ func GetLengthOfGeographicalAreaCode(number *PhoneNumber) int {
// format, if there is a subscriber number part that follows. An example
// of how this could be used:
//
// PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
// PhoneNumber number = phoneUtil.parse("18002530000", "US");
// String nationalSignificantNumber = phoneUtil.GetNationalSignificantNumber(number);
// String nationalDestinationCode;
// String subscriberNumber;
// PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
// PhoneNumber number = phoneUtil.parse("18002530000", "US");
// String nationalSignificantNumber = phoneUtil.GetNationalSignificantNumber(number);
// String nationalDestinationCode;
// String subscriberNumber;
//
// int nationalDestinationCodeLength =
// phoneUtil.GetLengthOfNationalDestinationCode(number);
// if nationalDestinationCodeLength > 0 {
// nationalDestinationCode = nationalSignificantNumber.substring(0,
// nationalDestinationCodeLength);
// subscriberNumber = nationalSignificantNumber.substring(
// nationalDestinationCodeLength);
// } else {
// nationalDestinationCode = "";
// subscriberNumber = nationalSignificantNumber;
// }
// int nationalDestinationCodeLength =
// phoneUtil.GetLengthOfNationalDestinationCode(number);
// if nationalDestinationCodeLength > 0 {
// nationalDestinationCode = nationalSignificantNumber.substring(0,
// nationalDestinationCodeLength);
// subscriberNumber = nationalSignificantNumber.substring(
// nationalDestinationCodeLength);
// } else {
// nationalDestinationCode = "";
// subscriberNumber = nationalSignificantNumber;
// }
//
// Refer to the unittests to see the difference between this function and
// GetLengthOfGeographicalAreaCode().
Expand Down Expand Up @@ -1579,16 +1585,16 @@ func hasFormattingPatternForNumber(number *PhoneNumber) bool {
//
// Caveats:
//
// - 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.
// - 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.
// - 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.
// - 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.
func FormatOutOfCountryKeepingAlphaChars(
number *PhoneNumber,
regionCallingFrom string) string {
Expand Down Expand Up @@ -2379,21 +2385,21 @@ func testNumberLength(number string, metadata *PhoneMetadata, numberType PhoneNu
// Check whether a phone number is a possible number. It provides a more
// lenient check than IsValidNumber() in the following sense:
//
// - It only checks the length of phone numbers. In particular, it
// doesn't check starting digits of the number.
// - 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.
// - 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.
// - It only checks the length of phone numbers. In particular, it
// doesn't check starting digits of the number.
// - 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.
// - 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.
func IsPossibleNumberWithReason(number *PhoneNumber) ValidationResult {
nationalNumber := GetNationalSignificantNumber(number)
countryCode := int(number.GetCountryCode())
Expand Down Expand Up @@ -2506,17 +2512,17 @@ var ErrTooShortAfterIDD = errors.New("phone number had an IDD, but " +
// return zero if no country calling code is considered to be present.
// Country calling codes are extracted in the following ways:
//
// - by stripping the international dialing prefix of the region 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 calling 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 calling code of this region, validation will be reattempted
// after stripping this country calling code. If this number is considered a
// possible number, then the first digits will be considered the country
// calling code and removed as such.
// - by stripping the international dialing prefix of the region 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 calling 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 calling code of this region, validation will be reattempted
// after stripping this country calling code. If this number is considered a
// possible number, then the first digits will be considered the country
// calling code and removed as such.
//
// It will throw a NumberParseException if the number starts with a '+' but
// the country calling code supplied after this does not match that of any
Expand Down Expand Up @@ -3398,6 +3404,17 @@ func GetCarrierForNumber(number *PhoneNumber, lang string) (string, error) {
return carrier, err
}

// GetSafeCarrierDisplayNameForNumber Gets the name of the carrier for the given phone number
// only when it is 'safe' to display to users.
// A carrier name is considered safe if the number is valid and
// for a region that doesn't support mobile number portability .
func GetSafeCarrierDisplayNameForNumber(phoneNumber *PhoneNumber, lang string) (string, error) {
if IsMobileNumberPortableRegion(GetRegionCodeForNumber(phoneNumber)) {
return "", nil
}
return GetCarrierForNumber(phoneNumber, lang)
}

// GetCarrierWithPrefixForNumber returns the carrier we believe the number belongs to, as well as
// its prefix. Note due to number porting this is only a guess, there is no guarantee to its accuracy.
func GetCarrierWithPrefixForNumber(number *PhoneNumber, lang string) (string, int, error) {
Expand Down
24 changes: 24 additions & 0 deletions phonenumbers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1613,6 +1613,30 @@ func TestRegexCacheStrict(t *testing.T) {
}
}

func TestGetSafeCarrierDisplayNameForNumber(t *testing.T) {
tests := []struct {
num string
lang string
expected string
}{
{num: "+447387654321", lang: "en", expected: ""},
{num: "+244917654321", lang: "en", expected: "Movicel"},
}
for _, test := range tests {
number, err := Parse(test.num, "ZZ")
if err != nil {
t.Errorf("Failed to parse number %s: %s", test.num, err)
}
carrier, err := GetSafeCarrierDisplayNameForNumber(number, test.lang)
if err != nil {
t.Errorf("Failed to getSafeCarrierDisplayNameForNumber for the number %s: %s", test.num, err)
}
if test.expected != carrier {
t.Errorf("Expected '%s', got '%s' for '%s'", test.expected, carrier, test.num)
}
}
}

func s(str string) *string {
return &str
}

0 comments on commit bc8db54

Please sign in to comment.