public static void SetLeadingDigitsPatterns(XElement numberFormatElement, NumberFormat.Builder format) { foreach (XElement e in numberFormatElement.GetElementsByTagName(LEADING_DIGITS)) { format.AddLeadingDigitsPattern(ValidateRE(e.Value, true)); } }
/** * Extracts the pattern for international format. If there is no intlFormat, default to using the * national format. If the intlFormat is set to "NA" the intlFormat should be ignored. * * @throws RuntimeException if multiple intlFormats have been encountered. * @return whether an international number format is defined. */ public static bool LoadInternationalFormat(PhoneMetadata.Builder metadata, XElement numberFormatElement, string nationalFormat) { var intlFormat = new NumberFormat.Builder(); SetLeadingDigitsPatterns(numberFormatElement, intlFormat); intlFormat.SetPattern(numberFormatElement.GetAttribute(PATTERN)); var intlFormatPattern = numberFormatElement.GetElementsByTagName(INTL_FORMAT).ToList(); var hasExplicitIntlFormatDefined = false; if (intlFormatPattern.Count > 1) { throw new Exception("Invalid number of intlFormat patterns for country: " + metadata.Id); } if (intlFormatPattern.Count == 0) { // Default to use the same as the national pattern if none is defined. intlFormat.SetFormat(nationalFormat); } else { var intlFormatPatternValue = intlFormatPattern.First().Value; intlFormat.SetFormat(intlFormatPatternValue); hasExplicitIntlFormatDefined = true; } if (intlFormat.HasFormat) { metadata.AddIntlNumberFormat(intlFormat); } return(hasExplicitIntlFormatDefined); }
/** * Extracts the available formats from the provided DOM element. If it does not contain any * nationalPrefixFormattingRule, the one passed-in is retained. The nationalPrefix, * nationalPrefixFormattingRule and nationalPrefixOptionalWhenFormatting values are provided from * the parent (territory) element. */ public static void LoadAvailableFormats(PhoneMetadata.Builder metadata, XElement element, string nationalPrefix, string nationalPrefixFormattingRule, bool nationalPrefixOptionalWhenFormatting) { var carrierCodeFormattingRule = ""; if (element.HasAttribute(CARRIER_CODE_FORMATTING_RULE)) carrierCodeFormattingRule = ValidateRE( GetDomesticCarrierCodeFormattingRuleFromElement(element, nationalPrefix)); var numberFormatElements = element.GetElementsByTagName(NUMBER_FORMAT).ToList(); var hasExplicitIntlFormatDefined = false; var numOfFormatElements = numberFormatElements.Count; if (numOfFormatElements > 0) { foreach (var numberFormatElement in numberFormatElements) { var format = new NumberFormat.Builder(); if (numberFormatElement.HasAttribute(NATIONAL_PREFIX_FORMATTING_RULE)) { format.SetNationalPrefixFormattingRule( GetNationalPrefixFormattingRuleFromElement(numberFormatElement, nationalPrefix)); format.SetNationalPrefixOptionalWhenFormatting( numberFormatElement.HasAttribute(NATIONAL_PREFIX_OPTIONAL_WHEN_FORMATTING)); } else { format.SetNationalPrefixFormattingRule(nationalPrefixFormattingRule); format.SetNationalPrefixOptionalWhenFormatting(nationalPrefixOptionalWhenFormatting); } if (numberFormatElement.HasAttribute("carrierCodeFormattingRule")) format.SetDomesticCarrierCodeFormattingRule(ValidateRE( GetDomesticCarrierCodeFormattingRuleFromElement( numberFormatElement, nationalPrefix))); else format.SetDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule); // Extract the pattern for the national format. var nationalFormat = LoadNationalFormat(metadata, numberFormatElement, format); metadata.AddNumberFormat(format); if (LoadInternationalFormat(metadata, numberFormatElement, nationalFormat)) hasExplicitIntlFormatDefined = true; } // Only a small number of regions need to specify the intlFormats in the xml. For the majority // of countries the intlNumberFormat metadata is an exact copy of the national NumberFormat // metadata. To minimize the size of the metadata file, we only keep intlNumberFormats that // actually differ in some way to the national formats. if (!hasExplicitIntlFormatDefined) metadata.ClearIntlNumberFormat(); } }
/** * Extracts the pattern for the national format. * * @throws RuntimeException if multiple or no formats have been encountered. * @return the national format string. */ public static string LoadNationalFormat(PhoneMetadata.Builder metadata, XElement numberFormatElement, NumberFormat.Builder format) { SetLeadingDigitsPatterns(numberFormatElement, format); format.SetPattern(ValidateRE(numberFormatElement.GetAttribute(PATTERN))); var formatPattern = numberFormatElement.GetElementsByTagName(FORMAT).ToList(); if (formatPattern.Count != 1) throw new Exception("Invalid number of format patterns for country: " + metadata.Id); var nationalFormat = formatPattern[0].Value; format.SetFormat(nationalFormat); return nationalFormat; }
/** * Extracts the pattern for the national format. * * @throws RuntimeException if multiple or no formats have been encountered. * @return the national format string. */ // @VisibleForTesting public static String LoadNationalFormat(PhoneMetadata.Builder metadata, XElement numberFormatElement, NumberFormat.Builder format) { SetLeadingDigitsPatterns(numberFormatElement, format); format.SetPattern(ValidateRE(numberFormatElement.GetAttribute(PATTERN))); var formatPattern = numberFormatElement.Elements(FORMAT).ToArray(); if (formatPattern.Count() != 1) { //LOGGER.log(Level.SEVERE, // "Only one format pattern for a numberFormat element should be defined."); throw new Exception("Invalid number of format patterns for country: " + metadata.Id); } String nationalFormat = formatPattern[0].Value; format.SetFormat(nationalFormat); return(nationalFormat); }
/** * Extracts the pattern for international format. If there is no intlFormat, default to using the * national format. If the intlFormat is set to "NA" the intlFormat should be ignored. * * @throws RuntimeException if multiple intlFormats have been encountered. * @return whether an international number format is defined. */ // @VisibleForTesting public static bool LoadInternationalFormat(PhoneMetadata.Builder metadata, XElement numberFormatElement, String nationalFormat) { var intlFormat = new NumberFormat.Builder(); SetLeadingDigitsPatterns(numberFormatElement, intlFormat); intlFormat.SetPattern(numberFormatElement.GetAttribute(PATTERN)); var intlFormatPattern = numberFormatElement.GetElementsByTagName(INTL_FORMAT); bool hasExplicitIntlFormatDefined = false; if (intlFormatPattern.Length > 1) { //LOGGER.log(Level.SEVERE, // "A maximum of one intlFormat pattern for a numberFormat element should be " + // "defined."); throw new Exception("Invalid number of intlFormat patterns for country: " + metadata.Id); } if (intlFormatPattern.Length == 0) { // Default to use the same as the national pattern if none is defined. intlFormat.SetFormat(nationalFormat); } else { String intlFormatPatternValue = intlFormatPattern[0].Value; if (!intlFormatPatternValue.Equals("NA")) { intlFormat.SetFormat(intlFormatPatternValue); } hasExplicitIntlFormatDefined = true; } if (intlFormat.HasFormat) { metadata.AddIntlNumberFormat(intlFormat); } return(hasExplicitIntlFormatDefined); }
/** * Extracts the pattern for international format. If there is no intlFormat, default to using the * national format. If the intlFormat is set to "NA" the intlFormat should be ignored. * * @throws RuntimeException if multiple intlFormats have been encountered. * @return whether an international number format is defined. */ // @VisibleForTesting public static bool LoadInternationalFormat(PhoneMetadata.Builder metadata, XmlElement numberFormatElement, String nationalFormat) { NumberFormat.Builder intlFormat = new NumberFormat.Builder(); SetLeadingDigitsPatterns(numberFormatElement, intlFormat); intlFormat.SetPattern(numberFormatElement.GetAttribute(PATTERN)); var intlFormatPattern = numberFormatElement.GetElementsByTagName(INTL_FORMAT); bool hasExplicitIntlFormatDefined = false; if (intlFormatPattern.Count > 1) { //LOGGER.log(Level.SEVERE, // "A maximum of one intlFormat pattern for a numberFormat element should be " + // "defined."); throw new Exception("Invalid number of intlFormat patterns for country: " + metadata.Id); } else if (intlFormatPattern.Count == 0) { // Default to use the same as the national pattern if none is defined. intlFormat.SetFormat(nationalFormat); } else { String intlFormatPatternValue = intlFormatPattern[0].InnerText; if (!intlFormatPatternValue.Equals("NA")) { intlFormat.SetFormat(intlFormatPatternValue); } hasExplicitIntlFormatDefined = true; } if (intlFormat.HasFormat) { metadata.AddIntlNumberFormat(intlFormat); } return hasExplicitIntlFormatDefined; }
/** * Extracts the available formats from the provided DOM element. If it does not contain any * nationalPrefixFormattingRule, the one passed-in is retained. The nationalPrefix, * nationalPrefixFormattingRule and nationalPrefixOptionalWhenFormatting values are provided from * the parent (territory) element. */ // @VisibleForTesting public static void LoadAvailableFormats(PhoneMetadata.Builder metadata, XmlElement element, String nationalPrefix, String nationalPrefixFormattingRule, bool nationalPrefixOptionalWhenFormatting) { String carrierCodeFormattingRule = ""; if (element.HasAttribute(CARRIER_CODE_FORMATTING_RULE)) { carrierCodeFormattingRule = ValidateRE( GetDomesticCarrierCodeFormattingRuleFromElement(element, nationalPrefix)); } var numberFormatElements = element.GetElementsByTagName(NUMBER_FORMAT); bool hasExplicitIntlFormatDefined = false; int numOfFormatElements = numberFormatElements.Count; if (numOfFormatElements > 0) { foreach (XmlElement numberFormatElement in numberFormatElements) { var format = new NumberFormat.Builder(); if (numberFormatElement.HasAttribute(NATIONAL_PREFIX_FORMATTING_RULE)) { format.SetNationalPrefixFormattingRule( GetNationalPrefixFormattingRuleFromElement(numberFormatElement, nationalPrefix)); format.SetNationalPrefixOptionalWhenFormatting( numberFormatElement.HasAttribute(NATIONAL_PREFIX_OPTIONAL_WHEN_FORMATTING)); } else { format.SetNationalPrefixFormattingRule(nationalPrefixFormattingRule); format.SetNationalPrefixOptionalWhenFormatting(nationalPrefixOptionalWhenFormatting); } if (numberFormatElement.HasAttribute("carrierCodeFormattingRule")) { format.SetDomesticCarrierCodeFormattingRule(ValidateRE( GetDomesticCarrierCodeFormattingRuleFromElement( numberFormatElement, nationalPrefix))); } else { format.SetDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule); } // Extract the pattern for the national format. String nationalFormat = LoadNationalFormat(metadata, numberFormatElement, format); metadata.AddNumberFormat(format); if (LoadInternationalFormat(metadata, numberFormatElement, nationalFormat)) { hasExplicitIntlFormatDefined = true; } } // Only a small number of regions need to specify the intlFormats in the xml. For the majority // of countries the intlNumberFormat metadata is an exact copy of the national NumberFormat // metadata. To minimize the size of the metadata file, we only keep intlNumberFormats that // actually differ in some way to the national formats. if (!hasExplicitIntlFormatDefined) { metadata.ClearIntlNumberFormat(); } } }
/** * Formats a phone number using the original phone number format that the number is parsed from. * The original format is embedded in the country_code_source field of the PhoneNumber object * passed in. If such information is missing, the number will be formatted into the NATIONAL * format by default. When the number contains a leading zero and this is unexpected for this * country, or we don't have a formatting pattern for the number, the method returns the raw input * when it is available. * * Note this method guarantees no digit will be inserted, removed or modified as a result of * formatting. * * @param number the phone number that needs to be formatted in its original number format * @param regionCallingFrom the region whose IDD needs to be prefixed if the original number * has one * @return the formatted phone number in its original number format */ public String FormatInOriginalFormat(PhoneNumber number, String regionCallingFrom) { if (number.HasRawInput && (HasUnexpectedItalianLeadingZero(number) || !HasFormattingPatternForNumber(number))) { // We check if we have the formatting pattern because without that, we might format the number // as a group without national prefix. return number.RawInput; } if (!number.HasCountryCodeSource) return Format(number, PhoneNumberFormat.NATIONAL); String formattedNumber; switch (number.CountryCodeSource) { case CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN: formattedNumber = Format(number, PhoneNumberFormat.INTERNATIONAL); break; case CountryCodeSource.FROM_NUMBER_WITH_IDD: formattedNumber = FormatOutOfCountryCallingNumber(number, regionCallingFrom); break; case CountryCodeSource.FROM_NUMBER_WITHOUT_PLUS_SIGN: formattedNumber = Format(number, PhoneNumberFormat.INTERNATIONAL).Substring(1); break; case CountryCodeSource.FROM_DEFAULT_COUNTRY: // Fall-through to default case. default: String regionCode = GetRegionCodeForCountryCode(number.CountryCode); // We strip non-digits from the NDD here, and from the raw input later, so that we can // compare them easily. String nationalPrefix = GetNddPrefixForRegion(regionCode, true /* strip non-digits */); String nationalFormat = Format(number, PhoneNumberFormat.NATIONAL); if (nationalPrefix == null || nationalPrefix.Length == 0) { // If the region doesn't have a national prefix at all, we can safely return the national // format without worrying about a national prefix being added. formattedNumber = nationalFormat; break; } // Otherwise, we check if the original number was entered with a national prefix. if (RawInputContainsNationalPrefix( number.RawInput, nationalPrefix, regionCode)) { // If so, we can safely return the national format. formattedNumber = nationalFormat; break; } PhoneMetadata metadata = GetMetadataForRegion(regionCode); String nationalNumber = GetNationalSignificantNumber(number); NumberFormat formatRule = ChooseFormattingPatternForNumber(metadata.NumberFormatList, nationalNumber); // When the format we apply to this number doesn't contain national prefix, we can just // return the national format. // TODO: Refactor the code below with the code in isNationalPrefixPresentIfRequired. String candidateNationalPrefixRule = formatRule.NationalPrefixFormattingRule; // We assume that the first-group symbol will never be _before_ the national prefix. int indexOfFirstGroup = candidateNationalPrefixRule.IndexOf("${1}"); if (indexOfFirstGroup <= 0) { formattedNumber = nationalFormat; break; } candidateNationalPrefixRule = candidateNationalPrefixRule.Substring(0, indexOfFirstGroup); candidateNationalPrefixRule = NormalizeDigitsOnly(candidateNationalPrefixRule); if (candidateNationalPrefixRule.Length == 0) { // National prefix not used when formatting this number. formattedNumber = nationalFormat; break; } // Otherwise, we need to remove the national prefix from our output. var numFormatCopy = new NumberFormat.Builder() .MergeFrom(formatRule) .ClearNationalPrefixFormattingRule() .Build(); List<NumberFormat> numberFormats = new List<NumberFormat>(1); numberFormats.Add(numFormatCopy); formattedNumber = FormatByPattern(number, PhoneNumberFormat.NATIONAL, numberFormats); break; } String rawInput = number.RawInput; // If no digit is inserted/removed/modified as a result of our formatting, we return the // formatted phone number; otherwise we return the raw input the user entered. return (formattedNumber != null && NormalizeHelper(formattedNumber, DIALLABLE_CHAR_MAPPINGS, true /* remove non matches */) .Equals(NormalizeHelper( rawInput, DIALLABLE_CHAR_MAPPINGS, true /* remove non matches */))) ? formattedNumber : rawInput; }
/** * 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 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 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; 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(); }
/** * Extracts the available formats from the provided DOM element. If it does not contain any * nationalPrefixFormattingRule, the one passed-in is retained. The nationalPrefix, * nationalPrefixFormattingRule and nationalPrefixOptionalWhenFormatting values are provided from * the parent (territory) element. */ public static void LoadAvailableFormats(PhoneMetadata.Builder metadata, XElement element, string nationalPrefix, string nationalPrefixFormattingRule, bool nationalPrefixOptionalWhenFormatting) { var carrierCodeFormattingRule = ""; if (element.HasAttribute(CARRIER_CODE_FORMATTING_RULE)) { carrierCodeFormattingRule = ValidateRE( GetDomesticCarrierCodeFormattingRuleFromElement(element, nationalPrefix)); } var availableFormats = element.Element("availableFormats"); var hasExplicitIntlFormatDefined = false; if (availableFormats != null && availableFormats.HasElements) { foreach (var numberFormatElement in availableFormats.Elements()) { var format = new NumberFormat.Builder(); if (numberFormatElement.HasAttribute(NATIONAL_PREFIX_FORMATTING_RULE)) { format.SetNationalPrefixFormattingRule( GetNationalPrefixFormattingRuleFromElement(numberFormatElement, nationalPrefix)); } else if (!nationalPrefixFormattingRule.Equals("")) { format.SetNationalPrefixFormattingRule(nationalPrefixFormattingRule); } if (numberFormatElement.HasAttribute(NATIONAL_PREFIX_OPTIONAL_WHEN_FORMATTING)) { format.SetNationalPrefixOptionalWhenFormatting( bool.Parse(numberFormatElement.Attribute(NATIONAL_PREFIX_OPTIONAL_WHEN_FORMATTING).Value)); } else if (format.NationalPrefixOptionalWhenFormatting != nationalPrefixOptionalWhenFormatting) { // Inherit from the parent field if it is not already the same as the default. format.SetNationalPrefixOptionalWhenFormatting(nationalPrefixOptionalWhenFormatting); } if (numberFormatElement.HasAttribute("carrierCodeFormattingRule")) { format.SetDomesticCarrierCodeFormattingRule(ValidateRE( GetDomesticCarrierCodeFormattingRuleFromElement( numberFormatElement, nationalPrefix))); } else if (!carrierCodeFormattingRule.Equals("")) { format.SetDomesticCarrierCodeFormattingRule(carrierCodeFormattingRule); } // Extract the pattern for the national format. var nationalFormat = LoadNationalFormat(metadata, numberFormatElement, format); metadata.AddNumberFormat(format); if (LoadInternationalFormat(metadata, numberFormatElement, nationalFormat)) { hasExplicitIntlFormatDefined = true; } } // Only a small number of regions need to specify the intlFormats in the xml. For the majority // of countries the intlNumberFormat metadata is an exact copy of the national NumberFormat // metadata. To minimize the size of the metadata file, we only keep intlNumberFormats that // actually differ in some way to the national formats. if (!hasExplicitIntlFormatDefined) { metadata.ClearIntlNumberFormat(); } } }