Mackovich
Mackovich

Reputation: 3573

libphonenumber - formatting phone numbers without knowing the country code

I have heard a lot of good from what appears to be an awesome library but I find myself in a delicate situation.

This is the first project I have ever worked where I am supposed to store phone numbers in a database.

I have read a bit about the E.164 format and I do intend to store all phone numbers using this format in my database.

The problem I am facing, is the data source. I have no control over the data source. All I known is that I am receiving a bunch of phone numbers and their format is not consistent. Some have the international extension, some don't. Some have parenthesis, hyphens, leading 0, etc. some don't.

How could I possibly extract the phone numbers from said source, format them into E.164 so that I can store them safely ?

I have tried using the PhoneNumberUtil#parse() method without providing the country code, since I don't have access to that information.

Have a look at the following example:

System.out.printLn("Number -> " + phoneNumberUtil.parse("00336555233634", null).toString())

Error type: INVALID_COUNTRY_CODE. Missing or invalid default region.

In my example, the number is that of french mobile phone. The two starting 0 works if you dial from outside France, I believe.

But the library cannot understand it as it is laking the country code. Does that mean there does not exist a way to understand where that particular phone number is coming from ?

The documentation seems clear about it :

public PhoneNumber parse(CharSequence numberToParse, String defaultRegion)

@param defaultRegion region 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 region supplied. If the number * is guaranteed to start with a '+' followed by the country calling code, then RegionCode.ZZ * or null can be supplied.

So, if add the +33

System.out.printLn("Number -> " + phoneNumberUtil.parse("+336555233634", null).toString())

Then naturally the result is:

Number -> Country Code: 33 National Number: 336555233634

What should / can I do if the end-user supplies my app with phone numbers that do not start with + ? I cannot believe I am the only one if this situation.

Thanks for the help !

Upvotes: 21

Views: 22411

Answers (3)

ibrust
ibrust

Reputation: 170

You could just try looping through all the country codes and initializing the PhoneNumber until you get !== undefined. You don't actually need to know the country code, just whether it can be parsed and it's a valid number potentially, right?

You could order this Array of country codes by their likelihood, i.e. put US first followed by other North American ones, etc. (or however it makes sense for you).

I ended up just looping through a subset of nations which do business in our industry, i.e.

const countryCodes = ['US', 'CA', 'MX', 'CN', 'GB', 'NL', 'DE', 'JP', 'ES', 'BE', 'CH', 'SE', 'IT', 'AR', 'BR', 'NO', 'FR', 'TW', 'CO', 'IE', 'CL', 'PE', 'SG', 'FI', 'AU', 'TR']

I'm doing real-time textfield validation with this method, and after testing it I can tell you performance is no issue.

Upvotes: 0

Joop Eggen
Joop Eggen

Reputation: 109547

Store the original phone numbers in a non-operational column RAW_PHONE, and the canonical E.164 standardized number in you PHONE column ("00" -> "+", "(" -> "" and such). This way you are prepared for check lists and manual corrections, and improving the conversion.

Imaginable is having two phone columns in the table, and as second value just another extension -203.

Better not to fill the strict field if the conversion fails on country code or is otherwise dubious. Is the default France, or what when the user lives in Belgium? One might argue that the source (location) of user registration determines the default country code.

Upvotes: 1

VinuBibin
VinuBibin

Reputation: 785

You need to use E164Format only. Here I took Norway as example

I have test case which tests and give the phone number in one format.

public static String getE164FormattedMobileNumber(String mobile, String locale)
            throws PhoneNumberFormatException {
        try {
            PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
            PhoneNumber phoneProto = phoneUtil.parse(mobile, locale);
            if (phoneUtil.isValidNumber(phoneProto)
                    && phoneUtil.isPossibleNumberForType(phoneProto, PhoneNumberType.MOBILE)) {
                return phoneUtil.format(phoneProto, PhoneNumberFormat.E164);
            }
            throw new PhoneNumberFormatException(
                    "Mobile number is invalid with the provided locale");
        } catch (NumberParseException e) {
            throw new PhoneNumberFormatException("Error in parsing mobile number", e);
        }
    }

and the test case as follows.

// this is the test mobile used
    private String expectedMobileNumber = "+4746205615";
private List<String> sucessMobileNumbers;

private List<String> failMobileNumbers;

public PhoneNumberE164FormatTest() {
    sucessMobileNumbers =
            Arrays.asList(
                    "46205615",
                    "004746205615",
                    "+4746205615",
                    "4746205615",
                    "46205615",
                    "+47 46205615",
                    "462 05 615");
    failMobileNumbers = Arrays.asList("abcdsds3434", "abcdsds343?#4", "21448410", "9946739087");
}

@Test
public void e164FormattedMobileNumbersSucessCase() throws PhoneNumberFormatException {

    for (String mobileNumber : sucessMobileNumbers) {
        Assert.assertEquals(
                expectedMobileNumber,
                (PhoneNumberUtils.getE164FormattedMobileNumber(mobileNumber, NO)));
    }
}

@Test(expected = PhoneNumberFormatException.class)
public void e164FormattedMobileNumbersFailCase() throws PhoneNumberFormatException {
    for (String mobileNumber : failMobileNumbers) {
        PhoneNumberUtils.getE164FormattedMobileNumber(mobileNumber, NO);
    }
}

Upvotes: 2

Related Questions