/** * Formats a phone number in the specified format using client-defined formatting rules. Note that * if the phone number has a country calling code of zero or an otherwise invalid country calling * code, we cannot work out things like whether there should be a national prefix applied, or how * to format extensions, so we return the national significant number with no formatting applied. * * @param number the phone number to be formatted * @param numberFormat the format the phone number should be formatted into * @param userDefinedFormats formatting rules specified by clients * @return the formatted phone number */ public String FormatByPattern(PhoneNumber number, PhoneNumberFormat numberFormat, List<NumberFormat> userDefinedFormats) { int countryCallingCode = number.CountryCode; var nationalSignificantNumber = GetNationalSignificantNumber(number); // Note getRegionCodeForCountryCode() is used because formatting information for regions which // share a country calling code is contained by only one region for performance reasons. For // example, for NANPA regions it will be contained in the metadata for US. var regionCode = GetRegionCodeForCountryCode(countryCallingCode); if (!HasValidCountryCallingCode(countryCallingCode)) return nationalSignificantNumber; PhoneMetadata metadata = GetMetadataForRegionOrCallingCode(countryCallingCode, regionCode); StringBuilder formattedNumber = new StringBuilder(20); NumberFormat formattingPattern = ChooseFormattingPatternForNumber(userDefinedFormats, nationalSignificantNumber); if (formattingPattern == null) { // If no pattern above is matched, we format the number as a whole. formattedNumber.Append(nationalSignificantNumber); } else { var numFormatCopy = new NumberFormat.Builder(); // Before we do a replacement of the national prefix pattern $NP with the national prefix, we // need to copy the rule so that subsequent replacements for different numbers have the // appropriate national prefix. numFormatCopy.MergeFrom(formattingPattern); String nationalPrefixFormattingRule = formattingPattern.NationalPrefixFormattingRule; if (nationalPrefixFormattingRule.Length > 0) { String nationalPrefix = metadata.NationalPrefix; if (nationalPrefix.Length > 0) { // Replace $NP with national prefix and $FG with the first group ($1). nationalPrefixFormattingRule = NP_PATTERN.Replace(nationalPrefixFormattingRule, nationalPrefix, 1); nationalPrefixFormattingRule = FG_PATTERN.Replace(nationalPrefixFormattingRule, "$$1", 1); numFormatCopy.SetNationalPrefixFormattingRule(nationalPrefixFormattingRule); } else { // We don't want to have a rule for how to format the national prefix if there isn't one. numFormatCopy.ClearNationalPrefixFormattingRule(); } } formattedNumber.Append( FormatNsnUsingPattern(nationalSignificantNumber, numFormatCopy.Build(), numberFormat)); } MaybeAppendFormattedExtension(number, metadata, numberFormat, formattedNumber); PrefixNumberWithCountryCallingCode(countryCallingCode, numberFormat, formattedNumber); return formattedNumber.ToString(); }
/** * Formats a phone number in the specified format using client-defined formatting rules. Note that * if the phone number has a country calling code of zero or an otherwise invalid country calling * code, we cannot work out things like whether there should be a national prefix applied, or how * to format extensions, so we return the national significant number with no formatting applied. * * @param number the phone number to be formatted * @param numberFormat the format the phone number should be formatted into * @param userDefinedFormats formatting rules specified by clients * @return the formatted phone number */ public String FormatByPattern(PhoneNumber number, PhoneNumberFormat numberFormat, List<NumberFormat> userDefinedFormats) { int countryCallingCode = number.CountryCode; var nationalSignificantNumber = GetNationalSignificantNumber(number); // Note getRegionCodeForCountryCode() is used because formatting information for regions which // share a country calling code is contained by only one region for performance reasons. For // example, for NANPA regions it will be contained in the metadata for US. var regionCode = GetRegionCodeForCountryCode(countryCallingCode); if (!HasValidRegionCode(regionCode, countryCallingCode, nationalSignificantNumber)) return nationalSignificantNumber; var userDefinedFormatsCopy = new List<NumberFormat>(userDefinedFormats.Count); foreach (var numFormat in userDefinedFormats) { var nationalPrefixFormattingRule = numFormat.NationalPrefixFormattingRule; if (nationalPrefixFormattingRule.Length > 0) { // Before we do a replacement of the national prefix pattern $NP with the national prefix, // we need to copy the rule so that subsequent replacements for different numbers have the // appropriate national prefix. var numFormatCopy = new NumberFormat.Builder(); numFormatCopy.MergeFrom(numFormat); var nationalPrefix = GetMetadataForRegion(regionCode).NationalPrefix; if (nationalPrefix.Length > 0) { // Replace $NP with national prefix and $FG with the first group ($1). nationalPrefixFormattingRule = NP_PATTERN.Replace(nationalPrefixFormattingRule, nationalPrefix, 1); nationalPrefixFormattingRule = FG_PATTERN.Replace(nationalPrefixFormattingRule, "$$1", 1); numFormatCopy.SetNationalPrefixFormattingRule(nationalPrefixFormattingRule); } else { // We don't want to have a rule for how to format the national prefix if there isn't one. numFormatCopy.ClearNationalPrefixFormattingRule(); } userDefinedFormatsCopy.Add(numFormatCopy.Build()); } else { // Otherwise, we just add the original rule to the modified list of formats. userDefinedFormatsCopy.Add(numFormat); } } var formattedNumber = new StringBuilder(FormatAccordingToFormats(nationalSignificantNumber, userDefinedFormatsCopy, numberFormat)); MaybeGetFormattedExtension(number, regionCode, numberFormat, formattedNumber); FormatNumberByFormat(countryCallingCode, numberFormat, formattedNumber); return formattedNumber.ToString(); }
/** * Formats a phone number for out-of-country dialing purposes. * * Note that in this version, if the number was entered originally using alpha characters and * this version of the number is stored in raw_input, this representation of the number will be * used rather than the digit representation. Grouping information, as specified by characters * such as "-" and " ", will be retained. * * <p><b>Caveats:</b></p> * <ul> * <li> 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. * <li> 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. * </ul> * * @param number the phone number that needs to be formatted * @param regionCallingFrom the region where the call is being placed * @return the formatted phone number */ public String FormatOutOfCountryKeepingAlphaChars(PhoneNumber number, String regionCallingFrom) { var rawInput = number.RawInput; // If there is no raw input, then we can't keep alpha characters because there aren't any. // In this case, we return formatOutOfCountryCallingNumber. if (rawInput.Length == 0) return FormatOutOfCountryCallingNumber(number, regionCallingFrom); int countryCode = number.CountryCode; if (!HasValidCountryCallingCode(countryCode)) return rawInput; // Strip any prefix such as country calling code, IDD, that was present. We do this by comparing // the number in raw_input with the parsed number. // To do this, first we normalize punctuation. We retain number grouping symbols such as " " // only. rawInput = NormalizeHelper(rawInput, ALL_PLUS_NUMBER_GROUPING_SYMBOLS, true); // Now we trim everything before the first three digits in the parsed number. We choose three // because all valid alpha numbers have 3 digits at the start - if it does not, then we don't // trim anything at all. Similarly, if the national number was less than three digits, we don't // trim anything at all. var nationalNumber = GetNationalSignificantNumber(number); if (nationalNumber.Length > 3) { int firstNationalNumberDigit = rawInput.IndexOf(nationalNumber.Substring(0, 3)); if (firstNationalNumberDigit != -1) rawInput = rawInput.Substring(firstNationalNumberDigit); } var metadataForRegionCallingFrom = GetMetadataForRegion(regionCallingFrom); if (countryCode == NANPA_COUNTRY_CODE) { if (IsNANPACountry(regionCallingFrom)) return countryCode + " " + rawInput; } else if (IsValidRegionCode(regionCallingFrom) && countryCode == GetCountryCodeForValidRegion(regionCallingFrom)) { NumberFormat formattingPattern = ChooseFormattingPatternForNumber(metadataForRegionCallingFrom.NumberFormatList, nationalNumber); if (formattingPattern == null) // If no pattern above is matched, we format the original input. return rawInput; var newFormat = new NumberFormat.Builder(); newFormat.MergeFrom(formattingPattern); // The first group is the first group of digits that the user wrote together. newFormat.SetPattern("(\\d+)(.*)"); // Here we just concatenate them back together after the national prefix has been fixed. newFormat.SetFormat("$1$2"); // Now we format using this pattern instead of the default pattern, but with the national // prefix prefixed if necessary. // This will not work in the cases where the pattern (and not the leading digits) decide // whether a national prefix needs to be used, since we have overridden the pattern to match // anything, but that is not the case in the metadata to date. return FormatNsnUsingPattern(rawInput, newFormat.Build(), PhoneNumberFormat.NATIONAL); } String internationalPrefixForFormatting = ""; // If an unsupported region-calling-from is entered, or a country with multiple international // prefixes, the international format of the number is returned, unless there is a preferred // international prefix. if (metadataForRegionCallingFrom != null) { String internationalPrefix = metadataForRegionCallingFrom.InternationalPrefix; internationalPrefixForFormatting = UNIQUE_INTERNATIONAL_PREFIX.MatchAll(internationalPrefix).Success ? internationalPrefix : metadataForRegionCallingFrom.PreferredInternationalPrefix; } var formattedNumber = new StringBuilder(rawInput); String regionCode = GetRegionCodeForCountryCode(countryCode); PhoneMetadata metadataForRegion = GetMetadataForRegionOrCallingCode(countryCode, regionCode); MaybeAppendFormattedExtension(number, metadataForRegion, PhoneNumberFormat.INTERNATIONAL, formattedNumber); if (internationalPrefixForFormatting.Length > 0) { formattedNumber.Insert(0, " ").Insert(0, countryCode).Insert(0, " ") .Insert(0, internationalPrefixForFormatting); } else { // Invalid region entered as country-calling-from (so no metadata was found for it) or the // region chosen has multiple international dialling prefixes. // LOGGER.log(Level.WARNING, // "Trying to format number from invalid region " // + regionCallingFrom // + ". International formatting applied."); PrefixNumberWithCountryCallingCode(countryCode, PhoneNumberFormat.INTERNATIONAL, formattedNumber); } return formattedNumber.ToString(); }
/** * Formats a phone number for out-of-country dialing purposes. * * Note that in this version, if the number was entered originally using alpha characters and * this version of the number is stored in raw_input, this representation of the number will be * used rather than the digit representation. Grouping information, as specified by characters * such as "-" and " ", will be retained. * * <p><b>Caveats:</b></p> * <ul> * <li> 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. * <li> 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. * </ul> * * @param number the phone number that needs to be formatted * @param regionCallingFrom the region where the call is being placed * @return the formatted phone number */ public String FormatOutOfCountryKeepingAlphaChars(PhoneNumber number, String regionCallingFrom) { var rawInput = number.RawInput; // If there is no raw input, then we can't keep alpha characters because there aren't any. // In this case, we return formatOutOfCountryCallingNumber. if (rawInput.Length == 0) return FormatOutOfCountryCallingNumber(number, regionCallingFrom); int countryCode = number.CountryCode; var regionCode = GetRegionCodeForCountryCode(countryCode); if (!HasValidRegionCode(regionCode, countryCode, rawInput)) return rawInput; // Strip any prefix such as country calling code, IDD, that was present. We do this by comparing // the number in raw_input with the parsed number. // To do this, first we normalize punctuation. We retain number grouping symbols such as " " // only. rawInput = NormalizeHelper(rawInput, ALL_PLUS_NUMBER_GROUPING_SYMBOLS, true); // Now we trim everything before the first three digits in the parsed number. We choose three // because all valid alpha numbers have 3 digits at the start - if it does not, then we don't // trim anything at all. Similarly, if the national number was less than three digits, we don't // trim anything at all. var nationalNumber = GetNationalSignificantNumber(number); if (nationalNumber.Length > 3) { int firstNationalNumberDigit = rawInput.IndexOf(nationalNumber.Substring(0, 3)); if (firstNationalNumberDigit != -1) rawInput = rawInput.Substring(firstNationalNumberDigit); } var metadata = GetMetadataForRegion(regionCallingFrom); if (countryCode == NANPA_COUNTRY_CODE) { if (IsNANPACountry(regionCallingFrom)) return countryCode + " " + rawInput; } else if (countryCode == GetCountryCodeForRegion(regionCallingFrom)) { // Here we copy the formatting rules so we can modify the pattern we expect to match against. var availableFormats = new List<NumberFormat>(metadata.NumberFormatCount); foreach (var format in metadata.NumberFormatList) { var newFormat = new NumberFormat.Builder(); newFormat.MergeFrom(format); // The first group is the first group of digits that the user determined. newFormat.SetPattern("(\\d+)(.*)"); // Here we just concatenate them back together after the national prefix has been fixed. newFormat.SetFormat("$1$2"); availableFormats.Add(newFormat.Build()); } // Now we format using these patterns instead of the default pattern, but with the national // prefix prefixed if necessary, by choosing the format rule based on the leading digits // present in the unformatted national number. // This will not work in the cases where the pattern (and not the leading digits) decide // whether a national prefix needs to be used, since we have overridden the pattern to match // anything, but that is not the case in the metadata to date. return FormatAccordingToFormats(rawInput, availableFormats, PhoneNumberFormat.NATIONAL); } var internationalPrefix = metadata.InternationalPrefix; // For countries that have multiple international prefixes, the international format of the // number is returned, unless there is a preferred international prefix. String internationalPrefixForFormatting = UNIQUE_INTERNATIONAL_PREFIX.MatchAll(internationalPrefix).Success ? internationalPrefix : metadata.PreferredInternationalPrefix; var formattedNumber = new StringBuilder(rawInput); MaybeGetFormattedExtension(number, regionCode, PhoneNumberFormat.INTERNATIONAL, formattedNumber); if (internationalPrefixForFormatting.Length > 0) { formattedNumber.Insert(0, " ").Insert(0, countryCode.ToString(CultureInfo.InvariantCulture)).Insert(0, " ") .Insert(0, internationalPrefixForFormatting); } else { FormatNumberByFormat(countryCode, PhoneNumberFormat.INTERNATIONAL, formattedNumber); } return formattedNumber.ToString(); }