diff --git a/javascript/README b/javascript/README index d6d9b8bb3..df2edeed7 100644 --- a/javascript/README +++ b/javascript/README @@ -25,7 +25,7 @@ pages with your web browser: How to update: ============== -The JavaScript library is ported from the Java implementation (revision 448). +The JavaScript library is ported from the Java implementation (revision 469). When the Java project gets updated follow these steps to update the JavaScript project: diff --git a/javascript/i18n/phonenumbers/metadata.js b/javascript/i18n/phonenumbers/metadata.js index 36498d9a6..3e6473497 100644 --- a/javascript/i18n/phonenumbers/metadata.js +++ b/javascript/i18n/phonenumbers/metadata.js @@ -218,6 +218,7 @@ i18n.phonenumbers.metadata.countryCodeToRegionCodeMap = { ,878:["001"] ,880:["BD"] ,881:["001"] +,882:["001"] ,883:["001"] ,886:["TW"] ,888:["001"] @@ -675,7 +676,7 @@ i18n.phonenumbers.metadata.countryToMetadata = { ] ,"BF":[,[,,"[24-7]\\d{7}","\\d{8}"] ,[,,"(?:20(?:49|5[23]|9[016-9])|40(?:4[569]|55|7[0179])|50[34]\\d)\\d{4}","\\d{8}",,,"20491234"] -,[,,"(?:6(?:0[0-5]|[68]0)|7(?:[02-68]\\d|1[0-4689]|7[0-69]|9[0-689]))\\d{5}","\\d{8}",,,"70123456"] +,[,,"(?:6(?:0[0-7]|6[0-2]|8[01])|7(?:[02-68]\\d|1[0-4689]|7[0-69]|9[0-689]))\\d{5}","\\d{8}",,,"70123456"] ,[,,"NA","NA"] ,[,,"NA","NA"] ,[,,"NA","NA"] @@ -1328,21 +1329,26 @@ i18n.phonenumbers.metadata.countryToMetadata = { ,,[,,"000|112","\\d{3}",,,"112"] ,[,,"NA","NA"] ] -,"CZ":[,[,,"[2-9]\\d{8}","\\d{9}"] -,[,,"2\\d{8}|(?:3[1257-9]|4[16-9]|5[13-9])\\d{7}","\\d{9}",,,"212345678"] -,[,,"(?:60[1-8]|7(?:0[25]|[2379]\\d))\\d{6}","\\d{9}",,,"601123456"] -,[,,"800\\d{6}","\\d{9}",,,"800123456"] -,[,,"9(?:0[05689]|76)\\d{6}","\\d{9}",,,"900123456"] -,[,,"8[134]\\d{7}","\\d{9}",,,"811234567"] -,[,,"70[01]\\d{6}","\\d{9}",,,"700123456"] -,[,,"9[17]0\\d{6}","\\d{9}",,,"910123456"] -,"CZ",420,"00",,,,,,,,[[,"([2-9]\\d{2})(\\d{3})(\\d{3})","$1 $2 $3",,"","",0] +,"CZ":[,[,,"[2-8]\\d{8}|9\\d{8,11}","\\d{9,12}"] +,[,,"2\\d{8}|(?:3[1257-9]|4[16-9]|5[13-9])\\d{7}","\\d{9,12}",,,"212345678"] +,[,,"(?:60[1-8]|7(?:0[2-5]|[2379]\\d))\\d{6}","\\d{9,12}",,,"601123456"] +,[,,"800\\d{6}","\\d{9,12}",,,"800123456"] +,[,,"9(?:0[05689]|76)\\d{6}","\\d{9,12}",,,"900123456"] +,[,,"8[134]\\d{7}","\\d{9,12}",,,"811234567"] +,[,,"70[01]\\d{6}","\\d{9,12}",,,"700123456"] +,[,,"9[17]0\\d{6}","\\d{9,12}",,,"910123456"] +,"CZ",420,"00",,,,,,,,[[,"([2-9]\\d{2})(\\d{3})(\\d{3})","$1 $2 $3",["[2-8]|9[015-7]"] +,"","",0] +,[,"(96\\d)(\\d{3})(\\d{3})(\\d{3})","$1 $2 $3 $4",["96"] +,"","",0] +,[,"(9\\d)(\\d{3})(\\d{3})(\\d{3})","$1 $2 $3 $4",["9[36]"] +,"","",0] ] ,,[,,"NA","NA"] ,,,[,,"NA","NA"] -,[,,"9(?:5[056]|7[234])\\d{6}","\\d{9}",,,"972123456"] +,[,,"9(?:5[056]|7[234])\\d{6}","\\d{9,12}",,,"972123456"] ,,[,,"1(?:12|5[058])","\\d{3}",,,"112"] -,[,,"NA","NA"] +,[,,"9(?:3\\d{9}|6\\d{7,10})","\\d{9,12}",,,"93123456789"] ] ,"DE":[,[,,"[1-35-9]\\d{3,14}|4(?:[0-8]\\d{4,12}|9(?:4[1-8]|[0-35-7]\\d)\\d{2,7})","\\d{2,15}"] ,[,,"[246]\\d{5,13}|3(?:[03-9]\\d{4,13}|2\\d{9})|5(?:0[2-8]|[1256]\\d|[38][0-8]|4\\d{0,2}|[79][0-7])\\d{3,11}|7(?:0[2-8]|[1-9]\\d)\\d{3,10}|8(?:0[2-9]|[1-9]\\d)\\d{3,10}|9(?:0[6-9]|[1-9]\\d)\\d{3,10}","\\d{2,15}",,,"30123456"] @@ -1551,8 +1557,8 @@ i18n.phonenumbers.metadata.countryToMetadata = { ,[,,"NA","NA"] ] ,"ES":[,[,,"[5-9]\\d{8}","\\d{9}"] -,[,,"(?:8(?:[13]0|[28][0-8]|[47][1-9]|5[01346-9]|6[0457-9])|9(?:[1238][0-8]|[47][1-9]|[56]\\d))\\d{6}","\\d{9}",,,"810123456"] -,[,,"(?:6\\d|7[1-4])\\d{7}","\\d{9}",,,"612345678"] +,[,,"8(?:[13]0|[28][0-8]|[47][1-9]|5[01346-9]|6[0457-9])\\d{6}|9(?:[1238][0-8]\\d{6}|4[1-9]\\d{6}|5\\d{7}|6(?:[0-8]\\d{6}|9(?:0(?:[0-57-9]\\d{4}|6(?:0[0-8]|1[1-9]|[2-9]\\d)\\d{2})|[1-9]\\d{5}))|7(?:[124-9]\\d{2}|3(?:[0-8]\\d|9[1-9]))\\d{4})","\\d{9}",,,"810123456"] +,[,,"(?:6\\d{6}|7[1-4]\\d{5}|9(?:6906(?:09|10)|7390\\d{2}))\\d{2}","\\d{9}",,,"612345678"] ,[,,"[89]00\\d{6}","\\d{9}",,,"800123456"] ,[,,"80[367]\\d{6}","\\d{9}",,,"803123456"] ,[,,"90[12]\\d{6}","\\d{9}",,,"901123456"] @@ -2625,9 +2631,9 @@ i18n.phonenumbers.metadata.countryToMetadata = { ,,[,,"11[29]","\\d{3}",,,"112"] ,[,,"NA","NA"] ] -,"KW":[,[,,"[12569]\\d{6,7}|65816\\d{6}","\\d{7,8}|\\d{11}"] -,[,,"(?:18\\d|2(?:[23]\\d{2}|4[1-35-9]\\d|5(?:0[034]|[2-46]\\d|5[1-3]|7[1-7])))\\d{4}","\\d{7,8}",,,"22345678"] -,[,,"(?:5(?:0[0-2568]|5\\d)|6(?:0[034679]|5(?:[015-79]|8(?:[02-9]|1[0-57-9]))|6\\d|7[067]|9[069])|9(?:0[09]|4[049]|6[69]|[79]\\d))\\d{5}","\\d{8}",,,"50012345"] +,"KW":[,[,,"[12569]\\d{6,7}","\\d{7,8}"] +,[,,"(?:18\\d|2(?:[23]\\d{2}|4(?:[1-35-9]\\d|44)|5(?:0[034]|[2-46]\\d|5[1-3]|7[1-7])))\\d{4}","\\d{7,8}",,,"22345678"] +,[,,"(?:5(?:0[0-2568]|11|5\\d)|6(?:0[034679]|5[015-9]|6\\d|7[067]|9[069])|9(?:0[09]|4[049]|6[69]|[79]\\d))\\d{5}","\\d{8}",,,"50012345"] ,[,,"NA","NA"] ,[,,"NA","NA"] ,[,,"NA","NA"] @@ -2635,16 +2641,14 @@ i18n.phonenumbers.metadata.countryToMetadata = { ,[,,"NA","NA"] ,"KW",965,"00",,,,,,,,[[,"(\\d{4})(\\d{3,4})","$1 $2",["[1269]"] ,"","",0] -,[,"(5[05]\\d)(\\d{5})","$1 $2",["5"] -,"","",0] -,[,"(65816)(\\d{6})","$1 $2",["65816"] +,[,"(5[015]\\d)(\\d{5})","$1 $2",["5"] ,"","",0] ] ,,[,,"NA","NA"] ,,,[,,"NA","NA"] ,[,,"NA","NA"] ,,[,,"112","\\d{3}",,,"112"] -,[,,"65816\\d{6}","\\d{11}",,,"65816123456"] +,[,,"NA","NA"] ] ,"KY":[,[,,"[3589]\\d{9}","\\d{7}(?:\\d{3})?"] ,[,,"345(?:2(?:22|44)|444|6(?:23|38|40)|7(?:4[35-79]|6[6-9]|77)|8(?:00|1[45]|25|[48]8)|9(?:14|4[035-9]))\\d{4}","\\d{7}(?:\\d{3})?",,,"3452221234"] @@ -5148,6 +5152,35 @@ i18n.phonenumbers.metadata.countryToMetadata = { ,,[,,"NA","NA"] ,[,,"NA","NA"] ] +,"882":[,[,,"[13]\\d{6,11}","\\d{7,12}",,,"32123456"] +,[,,"NA","NA",,,"32123456"] +,[,,"3(?:2\\d{3}|37\\d{2}|4(?:2|7\\d{3}))\\d{4}","\\d{7,10}",,,"32123456"] +,[,,"NA","NA"] +,[,,"NA","NA"] +,[,,"NA","NA"] +,[,,"NA","NA"] +,[,,"1(?:3(?:0[0347]|[13][0139]|2[035]|4[013568]|6[0459]|7[06]|8[15678]|9[0689])\\d{4}|6\\d{5,10})|345\\d{7}","\\d{7,12}",,,"32123456"] +,"001",882,"",,,,,,,,[[,"(\\d{2})(\\d{4})(\\d{3})","$1 $2 $3",["3[23]"] +,"","",0] +,[,"(\\d{2})(\\d{5})","$1 $2",["16|342"] +,"","",0] +,[,"(\\d{2})(\\d{4})(\\d{4})","$1 $2 $3",["34[57]"] +,"","",0] +,[,"(\\d{3})(\\d{4})(\\d{4})","$1 $2 $3",["348"] +,"","",0] +,[,"(\\d{2})(\\d{2})(\\d{4})","$1 $2 $3",["1"] +,"","",0] +,[,"(\\d{2})(\\d{3,4})(\\d{4})","$1 $2 $3",["16"] +,"","",0] +,[,"(\\d{2})(\\d{4,5})(\\d{5})","$1 $2 $3",["16"] +,"","",0] +] +,,[,,"NA","NA"] +,,,[,,"NA","NA"] +,[,,"NA","NA"] +,,[,,"NA","NA"] +,[,,"348[57]\\d{7}","\\d{11}",,,"32123456"] +] ,"883":[,[,,"51\\d{7}(?:\\d{3})?","\\d{9}(?:\\d{3})?",,,"510012345"] ,[,,"NA","NA",,,"510012345"] ,[,,"NA","NA",,,"510012345"] diff --git a/javascript/i18n/phonenumbers/metadatafortesting.js b/javascript/i18n/phonenumbers/metadatafortesting.js index 8585b2986..9f64df73b 100644 --- a/javascript/i18n/phonenumbers/metadatafortesting.js +++ b/javascript/i18n/phonenumbers/metadatafortesting.js @@ -48,6 +48,7 @@ i18n.phonenumbers.metadata.countryCodeToRegionCodeMap = { ,262:["RE","YT"] ,376:["AD"] ,800:["001"] +,979:["001"] }; /** @@ -468,4 +469,20 @@ i18n.phonenumbers.metadata.countryToMetadata = { ,,[,,"NA","NA"] ,[,,"NA","NA"] ] +,"979":[,[,,"\\d{9}","\\d{9}",,,"123456789"] +,[,,"NA","NA",,,"123456789"] +,[,,"NA","NA",,,"123456789"] +,[,,"NA","NA"] +,[,,"\\d{9}","\\d{9}",,,"123456789"] +,[,,"NA","NA"] +,[,,"NA","NA"] +,[,,"NA","NA"] +,"001",979,"",,,,,,,1,[[,"(\\d)(\\d{4})(\\d{4})","$1 $2 $3",,"","",0] +] +,,[,,"NA","NA"] +,,,[,,"NA","NA"] +,[,,"NA","NA"] +,,[,,"NA","NA"] +,[,,"NA","NA"] +] }; diff --git a/javascript/i18n/phonenumbers/metadatalite.js b/javascript/i18n/phonenumbers/metadatalite.js index f4d2cbeee..74bed8f07 100644 --- a/javascript/i18n/phonenumbers/metadatalite.js +++ b/javascript/i18n/phonenumbers/metadatalite.js @@ -218,6 +218,7 @@ i18n.phonenumbers.metadata.countryCodeToRegionCodeMap = { ,878:["001"] ,880:["BD"] ,881:["001"] +,882:["001"] ,883:["001"] ,886:["TW"] ,888:["001"] @@ -675,7 +676,7 @@ i18n.phonenumbers.metadata.countryToMetadata = { ] ,"BF":[,[,,"[24-7]\\d{7}","\\d{8}"] ,[,,"(?:20(?:49|5[23]|9[016-9])|40(?:4[569]|55|7[0179])|50[34]\\d)\\d{4}","\\d{8}"] -,[,,"(?:6(?:0[0-5]|[68]0)|7(?:[02-68]\\d|1[0-4689]|7[0-69]|9[0-689]))\\d{5}","\\d{8}"] +,[,,"(?:6(?:0[0-7]|6[0-2]|8[01])|7(?:[02-68]\\d|1[0-4689]|7[0-69]|9[0-689]))\\d{5}","\\d{8}"] ,[,,"NA","NA"] ,[,,"NA","NA"] ,[,,"NA","NA"] @@ -1328,21 +1329,26 @@ i18n.phonenumbers.metadata.countryToMetadata = { ,,[,,"000|112","\\d{3}"] ,[,,"NA","NA"] ] -,"CZ":[,[,,"[2-9]\\d{8}","\\d{9}"] -,[,,"2\\d{8}|(?:3[1257-9]|4[16-9]|5[13-9])\\d{7}","\\d{9}"] -,[,,"(?:60[1-8]|7(?:0[25]|[2379]\\d))\\d{6}","\\d{9}"] -,[,,"800\\d{6}","\\d{9}"] -,[,,"9(?:0[05689]|76)\\d{6}","\\d{9}"] -,[,,"8[134]\\d{7}","\\d{9}"] -,[,,"70[01]\\d{6}","\\d{9}"] -,[,,"9[17]0\\d{6}","\\d{9}"] -,"CZ",420,"00",,,,,,,,[[,"([2-9]\\d{2})(\\d{3})(\\d{3})","$1 $2 $3",,"","",0] +,"CZ":[,[,,"[2-8]\\d{8}|9\\d{8,11}","\\d{9,12}"] +,[,,"2\\d{8}|(?:3[1257-9]|4[16-9]|5[13-9])\\d{7}","\\d{9,12}"] +,[,,"(?:60[1-8]|7(?:0[2-5]|[2379]\\d))\\d{6}","\\d{9,12}"] +,[,,"800\\d{6}","\\d{9,12}"] +,[,,"9(?:0[05689]|76)\\d{6}","\\d{9,12}"] +,[,,"8[134]\\d{7}","\\d{9,12}"] +,[,,"70[01]\\d{6}","\\d{9,12}"] +,[,,"9[17]0\\d{6}","\\d{9,12}"] +,"CZ",420,"00",,,,,,,,[[,"([2-9]\\d{2})(\\d{3})(\\d{3})","$1 $2 $3",["[2-8]|9[015-7]"] +,"","",0] +,[,"(96\\d)(\\d{3})(\\d{3})(\\d{3})","$1 $2 $3 $4",["96"] +,"","",0] +,[,"(9\\d)(\\d{3})(\\d{3})(\\d{3})","$1 $2 $3 $4",["9[36]"] +,"","",0] ] ,,[,,"NA","NA"] ,,,[,,"NA","NA"] -,[,,"9(?:5[056]|7[234])\\d{6}","\\d{9}"] +,[,,"9(?:5[056]|7[234])\\d{6}","\\d{9,12}"] ,,[,,"1(?:12|5[058])","\\d{3}"] -,[,,"NA","NA"] +,[,,"9(?:3\\d{9}|6\\d{7,10})","\\d{9,12}"] ] ,"DE":[,[,,"[1-35-9]\\d{3,14}|4(?:[0-8]\\d{4,12}|9(?:4[1-8]|[0-35-7]\\d)\\d{2,7})","\\d{2,15}"] ,[,,"[246]\\d{5,13}|3(?:[03-9]\\d{4,13}|2\\d{9})|5(?:0[2-8]|[1256]\\d|[38][0-8]|4\\d{0,2}|[79][0-7])\\d{3,11}|7(?:0[2-8]|[1-9]\\d)\\d{3,10}|8(?:0[2-9]|[1-9]\\d)\\d{3,10}|9(?:0[6-9]|[1-9]\\d)\\d{3,10}","\\d{2,15}"] @@ -1551,8 +1557,8 @@ i18n.phonenumbers.metadata.countryToMetadata = { ,[,,"NA","NA"] ] ,"ES":[,[,,"[5-9]\\d{8}","\\d{9}"] -,[,,"(?:8(?:[13]0|[28][0-8]|[47][1-9]|5[01346-9]|6[0457-9])|9(?:[1238][0-8]|[47][1-9]|[56]\\d))\\d{6}","\\d{9}"] -,[,,"(?:6\\d|7[1-4])\\d{7}","\\d{9}"] +,[,,"8(?:[13]0|[28][0-8]|[47][1-9]|5[01346-9]|6[0457-9])\\d{6}|9(?:[1238][0-8]\\d{6}|4[1-9]\\d{6}|5\\d{7}|6(?:[0-8]\\d{6}|9(?:0(?:[0-57-9]\\d{4}|6(?:0[0-8]|1[1-9]|[2-9]\\d)\\d{2})|[1-9]\\d{5}))|7(?:[124-9]\\d{2}|3(?:[0-8]\\d|9[1-9]))\\d{4})","\\d{9}"] +,[,,"(?:6\\d{6}|7[1-4]\\d{5}|9(?:6906(?:09|10)|7390\\d{2}))\\d{2}","\\d{9}"] ,[,,"[89]00\\d{6}","\\d{9}"] ,[,,"80[367]\\d{6}","\\d{9}"] ,[,,"90[12]\\d{6}","\\d{9}"] @@ -2625,9 +2631,9 @@ i18n.phonenumbers.metadata.countryToMetadata = { ,,[,,"11[29]","\\d{3}"] ,[,,"NA","NA"] ] -,"KW":[,[,,"[12569]\\d{6,7}|65816\\d{6}","\\d{7,8}|\\d{11}"] -,[,,"(?:18\\d|2(?:[23]\\d{2}|4[1-35-9]\\d|5(?:0[034]|[2-46]\\d|5[1-3]|7[1-7])))\\d{4}","\\d{7,8}"] -,[,,"(?:5(?:0[0-2568]|5\\d)|6(?:0[034679]|5(?:[015-79]|8(?:[02-9]|1[0-57-9]))|6\\d|7[067]|9[069])|9(?:0[09]|4[049]|6[69]|[79]\\d))\\d{5}","\\d{8}"] +,"KW":[,[,,"[12569]\\d{6,7}","\\d{7,8}"] +,[,,"(?:18\\d|2(?:[23]\\d{2}|4(?:[1-35-9]\\d|44)|5(?:0[034]|[2-46]\\d|5[1-3]|7[1-7])))\\d{4}","\\d{7,8}"] +,[,,"(?:5(?:0[0-2568]|11|5\\d)|6(?:0[034679]|5[015-9]|6\\d|7[067]|9[069])|9(?:0[09]|4[049]|6[69]|[79]\\d))\\d{5}","\\d{8}"] ,[,,"NA","NA"] ,[,,"NA","NA"] ,[,,"NA","NA"] @@ -2635,16 +2641,14 @@ i18n.phonenumbers.metadata.countryToMetadata = { ,[,,"NA","NA"] ,"KW",965,"00",,,,,,,,[[,"(\\d{4})(\\d{3,4})","$1 $2",["[1269]"] ,"","",0] -,[,"(5[05]\\d)(\\d{5})","$1 $2",["5"] -,"","",0] -,[,"(65816)(\\d{6})","$1 $2",["65816"] +,[,"(5[015]\\d)(\\d{5})","$1 $2",["5"] ,"","",0] ] ,,[,,"NA","NA"] ,,,[,,"NA","NA"] ,[,,"NA","NA"] ,,[,,"112","\\d{3}"] -,[,,"65816\\d{6}","\\d{11}"] +,[,,"NA","NA"] ] ,"KY":[,[,,"[3589]\\d{9}","\\d{7}(?:\\d{3})?"] ,[,,"345(?:2(?:22|44)|444|6(?:23|38|40)|7(?:4[35-79]|6[6-9]|77)|8(?:00|1[45]|25|[48]8)|9(?:14|4[035-9]))\\d{4}","\\d{7}(?:\\d{3})?"] @@ -5148,6 +5152,35 @@ i18n.phonenumbers.metadata.countryToMetadata = { ,,[,,"NA","NA"] ,[,,"NA","NA"] ] +,"882":[,[,,"[13]\\d{6,11}","\\d{7,12}"] +,[,,"NA","NA"] +,[,,"3(?:2\\d{3}|37\\d{2}|4(?:2|7\\d{3}))\\d{4}","\\d{7,10}"] +,[,,"NA","NA"] +,[,,"NA","NA"] +,[,,"NA","NA"] +,[,,"NA","NA"] +,[,,"1(?:3(?:0[0347]|[13][0139]|2[035]|4[013568]|6[0459]|7[06]|8[15678]|9[0689])\\d{4}|6\\d{5,10})|345\\d{7}","\\d{7,12}"] +,"001",882,"",,,,,,,,[[,"(\\d{2})(\\d{4})(\\d{3})","$1 $2 $3",["3[23]"] +,"","",0] +,[,"(\\d{2})(\\d{5})","$1 $2",["16|342"] +,"","",0] +,[,"(\\d{2})(\\d{4})(\\d{4})","$1 $2 $3",["34[57]"] +,"","",0] +,[,"(\\d{3})(\\d{4})(\\d{4})","$1 $2 $3",["348"] +,"","",0] +,[,"(\\d{2})(\\d{2})(\\d{4})","$1 $2 $3",["1"] +,"","",0] +,[,"(\\d{2})(\\d{3,4})(\\d{4})","$1 $2 $3",["16"] +,"","",0] +,[,"(\\d{2})(\\d{4,5})(\\d{5})","$1 $2 $3",["16"] +,"","",0] +] +,,[,,"NA","NA"] +,,,[,,"NA","NA"] +,[,,"NA","NA"] +,,[,,"NA","NA"] +,[,,"348[57]\\d{7}","\\d{11}"] +] ,"883":[,[,,"51\\d{7}(?:\\d{3})?","\\d{9}(?:\\d{3})?"] ,[,,"NA","NA"] ,[,,"NA","NA"] diff --git a/javascript/i18n/phonenumbers/phonenumberutil.js b/javascript/i18n/phonenumbers/phonenumberutil.js index ff759bc6e..b4a41bce7 100644 --- a/javascript/i18n/phonenumbers/phonenumberutil.js +++ b/javascript/i18n/phonenumbers/phonenumberutil.js @@ -103,7 +103,7 @@ i18n.phonenumbers.PhoneNumberUtil.NANPA_COUNTRY_CODE_ = 1; * @type {number} * @private */ -i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_ = 3; +i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_ = 2; /** @@ -195,14 +195,19 @@ i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_ = 'tel:'; /** - * We include the "+" here since RFC3966 format specifies that the context must - * be specified in international format. - * * @const * @type {string} * @private */ -i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_ = ';phone-context=+'; +i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_ = ';phone-context='; + + +/** + * @const + * @type {string} + * @private + */ +i18n.phonenumbers.PhoneNumberUtil.RFC3966_ISDN_SUBADDRESS_ = ';isub='; /** @@ -277,7 +282,7 @@ i18n.phonenumbers.PhoneNumberUtil.DIALLABLE_CHAR_MAPPINGS_ = { '7': '7', '8': '8', '9': '9', - '+': '+', + '+': i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN, '*': '*' }; @@ -516,8 +521,8 @@ i18n.phonenumbers.PhoneNumberUtil.UNIQUE_INTERNATIONAL_PREFIX_ = * @type {string} */ i18n.phonenumbers.PhoneNumberUtil.VALID_PUNCTUATION = - '-x\u2010-\u2015\u2212\u30FC\uFF0D-\uFF0F \u00A0\u200B\u2060\u3000()' + - '\uFF08\uFF09\uFF3B\uFF3D.\\[\\]/~\u2053\u223C\uFF5E'; + '-x\u2010-\u2015\u2212\u30FC\uFF0D-\uFF0F \u00A0\u00AD\u200B\u2060\u3000' + + '()\uFF08\uFF09\uFF3B\uFF3D.\\[\\]/~\u2053\u223C\uFF5E'; /** @@ -941,7 +946,7 @@ i18n.phonenumbers.PhoneNumberUtil.extractPossibleNumber = function(number) { /** * 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 + * all. At the moment, checks to see that the string begins with at least 2 * 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 @@ -1818,9 +1823,12 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.formatInOriginalFormat = // return the formatted phone number; otherwise we return the raw input the // user entered. return (formattedNumber != null && - i18n.phonenumbers.PhoneNumberUtil - .normalizeDigitsOnly(formattedNumber) == - i18n.phonenumbers.PhoneNumberUtil.normalizeDigitsOnly(rawInput)) ? + i18n.phonenumbers.PhoneNumberUtil.normalizeHelper_(formattedNumber, + i18n.phonenumbers.PhoneNumberUtil.DIALLABLE_CHAR_MAPPINGS_, + true /* remove non matches */) == + i18n.phonenumbers.PhoneNumberUtil.normalizeHelper_(rawInput, + i18n.phonenumbers.PhoneNumberUtil.DIALLABLE_CHAR_MAPPINGS_, + true /* remove non matches */)) ? formattedNumber : rawInput; }; @@ -3118,7 +3126,7 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.maybeExtractCountryCode = } if (countryCodeSource != i18n.phonenumbers.PhoneNumber.CountryCodeSource.FROM_DEFAULT_COUNTRY) { - if (fullNumber.getLength() < + if (fullNumber.getLength() <= i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_) { throw i18n.phonenumbers.Error.TOO_SHORT_AFTER_IDD; } @@ -3418,7 +3426,7 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.checkRegionForParsing_ = function( * * @param {?string} numberToParse number that we are attempting to parse. This * can contain formatting such as +, ( and -, as well as a phone number - * extension. + * extension. It can also be provided in RFC3966 format. * @param {?string} 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 @@ -3498,32 +3506,9 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.parseHelper_ = throw 'The string supplied was too long to parse'; } - /** @type {number} */ - var indexOfPhoneContext = numberToParse.indexOf( - i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_); /** @type {!goog.string.StringBuffer} */ var nationalNumber = new goog.string.StringBuffer(); - if (indexOfPhoneContext > 0) { - // Prefix the number with the phone context. The offset here is because the - // context we are expecting to match should start with a "+" sign, and we - // want to include this at the start of the number. - nationalNumber.append(numberToParse.substring( - indexOfPhoneContext + - i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_.length - 1)); - // Now append everything between the "tel:" prefix and the phone-context. - nationalNumber.append(numberToParse.substring( - numberToParse.indexOf( - i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_) + - i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_.length, - indexOfPhoneContext)); - // Note that phone-contexts that are URLs will not be parsed - - // isViablePhoneNumber will throw an exception below. - } else { - // Extract a possible number from the string passed in (this strips leading - // characters that could not be the start of a phone number.) - nationalNumber.append( - i18n.phonenumbers.PhoneNumberUtil.extractPossibleNumber(numberToParse)); - } + this.buildNationalNumberForParsing_(numberToParse, nationalNumber); if (!i18n.phonenumbers.PhoneNumberUtil.isViablePhoneNumber( nationalNumber.toString())) { @@ -3633,6 +3618,77 @@ i18n.phonenumbers.PhoneNumberUtil.prototype.parseHelper_ = }; +/** + * Converts numberToParse to a form that we can parse and write it to + * nationalNumber if it is written in RFC3966; otherwise extract a possible + * number out of it and write to nationalNumber. + * + * @param {?string} numberToParse number that we are attempting to parse. This + * can contain formatting such as +, ( and -, as well as a phone number + * extension. + * @param {!goog.string.StringBuffer} nationalNumber a string buffer for storing + * the national significant number. + * @private + */ +i18n.phonenumbers.PhoneNumberUtil.prototype.buildNationalNumberForParsing_ = + function(numberToParse, nationalNumber) { + + /** @type {number} */ + var indexOfPhoneContext = numberToParse.indexOf( + i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_); + if (indexOfPhoneContext > 0) { + var phoneContextStart = indexOfPhoneContext + + i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_.length; + // If the phone context contains a phone number prefix, we need to capture + // it, whereas domains will be ignored. + if (numberToParse.charAt(phoneContextStart) == + i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN) { + // Additional parameters might follow the phone context. If so, we will + // remove them here because the parameters after phone context are not + // important for parsing the phone number. + var phoneContextEnd = numberToParse.indexOf(';', phoneContextStart); + if (phoneContextEnd > 0) { + nationalNumber.append(numberToParse.substring(phoneContextStart, + phoneContextEnd)); + } else { + nationalNumber.append(numberToParse.substring(phoneContextStart)); + } + } + + // Now append everything between the "tel:" prefix and the phone-context. + // This should include the national number, an optional extension or + // isdn-subaddress component. + nationalNumber.append(numberToParse.substring( + numberToParse.indexOf( + i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_) + + i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_.length, + indexOfPhoneContext)); + } else { + // Extract a possible number from the string passed in (this strips leading + // characters that could not be the start of a phone number.) + nationalNumber.append( + i18n.phonenumbers.PhoneNumberUtil.extractPossibleNumber(numberToParse)); + } + + // Delete the isdn-subaddress and everything after it if it is present. + // Note extension won't appear at the same time with isdn-subaddress + // according to paragraph 5.3 of the RFC3966 spec, + /** @type {string} */ + var nationalNumberStr = nationalNumber.toString(); + var indexOfIsdn = nationalNumberStr.indexOf( + i18n.phonenumbers.PhoneNumberUtil.RFC3966_ISDN_SUBADDRESS_); + if (indexOfIsdn > 0) { + nationalNumber.clear(); + nationalNumber.append(nationalNumberStr.substring(0, indexOfIsdn)); + } + // If both phone context and isdn-subaddress are absent but other + // parameters are present, the parameters are left in nationalNumber. This + // is because we are concerned about deleting content from a potential + // number string when there is no strong evidence that the number is + // actually written in RFC3966. +}; + + /** * Takes two phone numbers and compares them for equality. * diff --git a/javascript/i18n/phonenumbers/phonenumberutil_test.js b/javascript/i18n/phonenumbers/phonenumberutil_test.js index 005749235..8c6023cd7 100644 --- a/javascript/i18n/phonenumbers/phonenumberutil_test.js +++ b/javascript/i18n/phonenumbers/phonenumberutil_test.js @@ -209,10 +209,19 @@ INTERNATIONAL_TOLL_FREE.setCountryCode(800); INTERNATIONAL_TOLL_FREE.setNationalNumber(12345678); +// We set this to be the same length as numbers for the other non-geographical +// country prefix that we have in our test metadata. However, this is not +// considered valid because they differ in their country calling code. /** @type {i18n.phonenumbers.PhoneNumber} */ var INTERNATIONAL_TOLL_FREE_TOO_LONG = new i18n.phonenumbers.PhoneNumber(); INTERNATIONAL_TOLL_FREE_TOO_LONG.setCountryCode(800); -INTERNATIONAL_TOLL_FREE_TOO_LONG.setNationalNumber(1234567890); +INTERNATIONAL_TOLL_FREE_TOO_LONG.setNationalNumber(123456789); + + +/** @type {i18n.phonenumbers.PhoneNumber} */ +var UNIVERSAL_PREMIUM_RATE = new i18n.phonenumbers.PhoneNumber(); +UNIVERSAL_PREMIUM_RATE.setCountryCode(979); +UNIVERSAL_PREMIUM_RATE.setNationalNumber(123456789); var RegionCode = i18n.phonenumbers.RegionCode; @@ -418,6 +427,8 @@ function testGetExampleNumber() { function testGetExampleNumberForNonGeoEntity() { assertTrue(INTERNATIONAL_TOLL_FREE.equals( phoneUtil.getExampleNumberForNonGeoEntity(800))); + assertTrue(UNIVERSAL_PREMIUM_RATE.equals( + phoneUtil.getExampleNumberForNonGeoEntity(979))); } function testConvertAlphaCharactersInNumber() { @@ -432,7 +443,7 @@ function testConvertAlphaCharactersInNumber() { function testNormaliseRemovePunctuation() { /** @type {string} */ - var inputNumber = '034-56&+#234'; + var inputNumber = '034-56&+#2\u00AD34'; /** @type {string} */ var expectedOutput = '03456234'; assertEquals('Conversion did not correctly remove punctuation', @@ -1214,6 +1225,18 @@ function testFormatInOriginalFormat() { assertEquals('0011 1 650 253 0000', phoneUtil.formatInOriginalFormat(outOfCountryNumberFromAU2, RegionCode.AU)); + + // Test the star sign is not removed from or added to the original input by + // this method. + /** @type {i18n.phonenumbers.PhoneNumber} */ + var starNumber = + phoneUtil.parseAndKeepRawInput('*1234', RegionCode.JP); + assertEquals('*1234', phoneUtil.formatInOriginalFormat(starNumber, + RegionCode.JP)); + /** @type {i18n.phonenumbers.PhoneNumber} */ + var numberWithoutStar = phoneUtil.parseAndKeepRawInput('1234', RegionCode.JP); + assertEquals('1234', phoneUtil.formatInOriginalFormat(numberWithoutStar, + RegionCode.JP)); } function testIsPremiumRate() { @@ -1241,6 +1264,8 @@ function testIsPremiumRate() { premiumRateNumber.setCountryCode(49); premiumRateNumber.setNationalNumber(90091234567); assertEquals(PNT.PREMIUM_RATE, phoneUtil.getNumberType(premiumRateNumber)); + assertEquals(PNT.PREMIUM_RATE, phoneUtil.getNumberType( + UNIVERSAL_PREMIUM_RATE)); } function testIsTollFree() { @@ -1343,6 +1368,7 @@ function testIsValidNumber() { assertTrue(phoneUtil.isValidNumber(IT_NUMBER)); assertTrue(phoneUtil.isValidNumber(GB_MOBILE)); assertTrue(phoneUtil.isValidNumber(INTERNATIONAL_TOLL_FREE)); + assertTrue(phoneUtil.isValidNumber(UNIVERSAL_PREMIUM_RATE)); /** @type {i18n.phonenumbers.PhoneNumber} */ var nzNumber = new i18n.phonenumbers.PhoneNumber(); @@ -1449,6 +1475,7 @@ function testGetRegionCodeForCountryCode() { assertEquals(RegionCode.GB, phoneUtil.getRegionCodeForCountryCode(44)); assertEquals(RegionCode.DE, phoneUtil.getRegionCodeForCountryCode(49)); assertEquals(RegionCode.UN001, phoneUtil.getRegionCodeForCountryCode(800)); + assertEquals(RegionCode.UN001, phoneUtil.getRegionCodeForCountryCode(979)); } function testGetRegionCodeForNumber() { @@ -1457,6 +1484,8 @@ function testGetRegionCodeForNumber() { assertEquals(RegionCode.GB, phoneUtil.getRegionCodeForNumber(GB_MOBILE)); assertEquals(RegionCode.UN001, phoneUtil.getRegionCodeForNumber(INTERNATIONAL_TOLL_FREE)); + assertEquals(RegionCode.UN001, + phoneUtil.getRegionCodeForNumber(UNIVERSAL_PREMIUM_RATE)); } function testGetCountryCodeForRegion() { @@ -1566,7 +1595,7 @@ function testIsPossibleNumberWithReason() { assertEquals(VR.IS_POSSIBLE, phoneUtil.isPossibleNumberWithReason(adNumber)); adNumber.setCountryCode(376); - adNumber.setNationalNumber(13); + adNumber.setNationalNumber(1); assertEquals(VR.TOO_SHORT, phoneUtil.isPossibleNumberWithReason(adNumber)); adNumber.setCountryCode(376); @@ -2018,7 +2047,17 @@ function testParseNationalNumber() { phoneUtil.parse('tel:331-6005;phone-context=+64-3', RegionCode.NZ))); assertTrue(NZ_NUMBER.equals( phoneUtil.parse('tel:331-6005;phone-context=+64-3', RegionCode.US))); - + // Test parsing RFC3966 format with optional user-defined parameters. The + // parameters will appear after the context if present. + assertTrue(NZ_NUMBER.equals( + phoneUtil.parse('tel:03-331-6005;phone-context=+64;a=%A1', + RegionCode.NZ))); + // Test parsing RFC3966 with an ISDN subaddress. + assertTrue(NZ_NUMBER.equals( + phoneUtil.parse('tel:03-331-6005;isub=12345;phone-context=+64', + RegionCode.NZ))); + assertTrue(NZ_NUMBER.equals( + phoneUtil.parse('tel:+64-3-331-6005;isub=12345', RegionCode.NZ))); // Testing international prefixes. // Should strip country calling code. assertTrue( @@ -2038,6 +2077,22 @@ function testParseNationalNumber() { assertTrue( NZ_NUMBER.equals(phoneUtil.parse('+ 00 64 3 331 6005', RegionCode.NZ))); + assertTrue(US_LOCAL_NUMBER.equals( + phoneUtil.parse('tel:253-0000;phone-context=www.google.com', + RegionCode.US))); + assertTrue(US_LOCAL_NUMBER.equals( + phoneUtil.parse('tel:253-0000;isub=12345;phone-context=www.google.com', + RegionCode.US))); + // This is invalid because no "+" sign is present as part of phone-context. + // The phone context is simply ignored in this case just as if it contains a + // domain. + assertTrue(US_LOCAL_NUMBER.equals( + phoneUtil.parse('tel:2530000;isub=12345;phone-context=1-650', + RegionCode.US))); + assertTrue(US_LOCAL_NUMBER.equals( + phoneUtil.parse('tel:2530000;isub=12345;phone-context=1234.com', + RegionCode.US))); + /** @type {i18n.phonenumbers.PhoneNumber} */ var nzNumber = new i18n.phonenumbers.PhoneNumber(); nzNumber.setCountryCode(64); @@ -2151,6 +2206,9 @@ function testParseNonAscii() { // Using a full-width plus sign. assertTrue(US_NUMBER.equals( phoneUtil.parse('\uFF0B1 (650) 253-0000', RegionCode.SG))); + // Using a soft hyphen U+00AD. + assertTrue(US_NUMBER.equals( + phoneUtil.parse('1 (650) 253\u00AD-0000', RegionCode.US))); // The whole number, including punctuation, is here represented in full-width // form. assertTrue(US_NUMBER.equals( @@ -2475,26 +2533,26 @@ function testFailedParseOnInvalidNumbers() { } try { /** @type {string} */ - var domainRfcPhoneContext = 'tel:555-1234;phone-context:www.google.com'; - phoneUtil.parse(domainRfcPhoneContext, RegionCode.US); - fail('Domain provided for phone context - should fail.'); + var domainRfcPhoneContext = 'tel:555-1234;phone-context=www.google.com'; + phoneUtil.parse(domainRfcPhoneContext, RegionCode.ZZ); + fail('"Unknown" region code not allowed: should fail.'); } catch (e) { // Expected this exception. assertEquals('Wrong error type stored in exception.', - i18n.phonenumbers.Error.NOT_A_NUMBER, + i18n.phonenumbers.Error.INVALID_COUNTRY_CODE, e); } try { // This is invalid because no '+' sign is present as part of phone-context. // This should not succeed in being parsed. /** @type {string} */ - var invalidRfcPhoneContext = 'tel:555-1234;phone-context:1-331'; - phoneUtil.parse(invalidRfcPhoneContext, RegionCode.US); - fail('No leading plus provided in phone context - should fail.'); + var invalidRfcPhoneContext = 'tel:555-1234;phone-context=1-331'; + phoneUtil.parse(invalidRfcPhoneContext, RegionCode.ZZ); + fail('"Unknown" region code not allowed: should fail.'); } catch (e) { // Expected this exception. assertEquals('Wrong error type stored in exception.', - i18n.phonenumbers.Error.NOT_A_NUMBER, + i18n.phonenumbers.Error.INVALID_COUNTRY_CODE, e); } } @@ -2514,12 +2572,17 @@ function testParseNumbersWithPlusWithNoRegion() { NZ_NUMBER.equals(phoneUtil.parse('+64 3 331 6005', null))); assertTrue( INTERNATIONAL_TOLL_FREE.equals(phoneUtil.parse('+800 1234 5678', null))); + assertTrue( + UNIVERSAL_PREMIUM_RATE.equals(phoneUtil.parse('+979 123 456 789', null))); // Test parsing RFC3966 format with a phone context. assertTrue(NZ_NUMBER.equals( phoneUtil.parse('tel:03-331-6005;phone-context=+64', RegionCode.ZZ))); assertTrue(NZ_NUMBER.equals( phoneUtil.parse(' tel:03-331-6005;phone-context=+64', RegionCode.ZZ))); + assertTrue(NZ_NUMBER.equals( + phoneUtil.parse('tel:03-331-6005;isub=12345;phone-context=+64', + RegionCode.ZZ))); // It is important that we set the carrier code to an empty string, since we // used ParseAndKeepRawInput and no carrier code was found. @@ -2762,6 +2825,9 @@ function testIsNumberMatchMatches() { phoneUtil.isNumberMatch('+643 331-6005', '+6433316005')); assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.EXACT_MATCH, phoneUtil.isNumberMatch('+64 3 331-6005', '+6433316005')); + assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.EXACT_MATCH, + phoneUtil.isNumberMatch('+64 3 331-6005', + 'tel:+64-3-331-6005;isub=123')); // Test alpha numbers. assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.EXACT_MATCH, phoneUtil.isNumberMatch('+1800 siX-Flags', '+1 800 7493 5247')); @@ -2824,6 +2890,9 @@ function testIsNumberMatchNonMatches() { assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.NO_MATCH, phoneUtil.isNumberMatch('+64 3 331-6005 extn 1234', '0116433316005#1235')); + assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.NO_MATCH, + phoneUtil.isNumberMatch('+64 3 331-6005 extn 1234', + 'tel:+64-3-331-6005;ext=1235')); // NSN matches, but extension is different - not the same number. assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.NO_MATCH, phoneUtil.isNumberMatch('+64 3 331-6005 ext.1235', @@ -2844,6 +2913,9 @@ function testIsNumberMatchNsnMatches() { // NSN matches. assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.NSN_MATCH, phoneUtil.isNumberMatch('+64 3 331-6005', '03 331 6005')); + assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.NSN_MATCH, + phoneUtil.isNumberMatch('+64 3 331-6005', + 'tel:03-331-6005;isub=1234;phone-context=abc.nz')); assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.NSN_MATCH, phoneUtil.isNumberMatch(NZ_NUMBER, '03 331 6005')); // Here the second number possibly starts with the country calling code for @@ -2883,12 +2955,26 @@ function testIsNumberMatchShortNsnMatches() { // numbers. assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, phoneUtil.isNumberMatch('+64 3 331-6005', '331 6005')); + assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, + phoneUtil.isNumberMatch('+64 3 331-6005', + 'tel:331-6005;phone-context=abc.nz')); + assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, + phoneUtil.isNumberMatch( + '+64 3 331-6005', + 'tel:331-6005;isub=1234;phone-context=abc.nz')); + assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, + phoneUtil.isNumberMatch( + '+64 3 331-6005', + 'tel:331-6005;isub=1234;phone-context=abc.nz;a=%A1')); // We did not know that the '0' was a national prefix since neither number has // a country code, so this is considered a SHORT_NSN_MATCH. assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, phoneUtil.isNumberMatch('3 331-6005', '03 331 6005')); assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, phoneUtil.isNumberMatch('3 331-6005', '331 6005')); + assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, + phoneUtil.isNumberMatch('3 331-6005', + 'tel:331-6005;phone-context=abc.nz')); assertEquals(i18n.phonenumbers.PhoneNumberUtil.MatchType.SHORT_NSN_MATCH, phoneUtil.isNumberMatch('3 331-6005', '+64 331 6005')); // Short NSN match with the country specified.