i18n_phone_numbers 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1237 @@
1
+ module I18nPhoneNumbers
2
+
3
+ module Util
4
+
5
+ # TODO : wait for ruby 1.9 to use unicode
6
+ #
7
+ # PLUS_CHARS_ = '+\uFF0B'
8
+ # VALID_DIGITS_ = '0-9\uFF10-\uFF19\u0660-\u0669\u06F0-\u06F9'
9
+ # VALID_PUNCTUATION = '-x\u2010-\u2015\u2212\u30FC\uFF0D-\uFF0F \u00A0\u200B\u2060\u3000()' +
10
+ # '\uFF08\uFF09\uFF3B\uFF3D.\\[\\]/~\u2053\u223C\uFF5E'
11
+
12
+ # TODO ? : support alpha in numbers ?
13
+ # VALID_ALPHA_ = 'A-Za-z'
14
+
15
+ # TODO ? : support extensions ?
16
+ # KNOWN_EXTN_PATTERNS_ = ...
17
+
18
+ PLUS_SIGN = '+'
19
+ STAR_SIGN = '*'
20
+ PLUS_CHARS_ = '+'
21
+ PLUS_CHARS_PATTERN = Regexp.compile('[' + PLUS_CHARS_ + ']+')
22
+ LEADING_PLUS_CHARS_PATTERN_ = Regexp.compile('\A[' + PLUS_CHARS_ + ']+')
23
+ VALID_DIGITS_ = '0-9'
24
+ VALID_ALPHA_ = '' # we don't support it yet
25
+ VALID_PUNCTUATION = '-x ().\\/\\[\\]~' # add "\-escapes" for [ and ] (will be further included in a [] group in another regexp)
26
+ KNOWN_EXTN_PATTERNS_ = '' # we don't support extensions yet
27
+ VALID_START_CHAR_PATTERN = Regexp.compile('[' + PLUS_CHARS_ + VALID_DIGITS_ + ']')
28
+ SECOND_NUMBER_START_PATTERN_ = /[\\\/] *x/
29
+ UNWANTED_END_CHAR_PATTERN_ = Regexp.compile('[^' + VALID_DIGITS_ + VALID_ALPHA_ + '#]+\z')
30
+ CAPTURING_DIGIT_PATTERN = /([0-9])/
31
+ # This was originally set to \\1 but there are some countries for which the
32
+ # first group is not used in the national pattern (e.g. Argentina) so the \\1
33
+ # group does not match correctly. Therefore, we use \d, so that the first
34
+ # group actually used in the pattern will be matched.
35
+ FIRST_GROUP_PATTERN_ = /(\\\d)/
36
+ NP_PATTERN_ = /\$NP/
37
+ FG_PATTERN_ = /\$FG/
38
+ CC_PATTERN_ = /\$CC/
39
+ REGION_CODE_FOR_NON_GEO_ENTITY = '001'
40
+
41
+ # Regular expression of viable phone numbers. This is location independent.
42
+ # -> plus_sign*(([punctuation]|[star])*[digits]){3,}([punctuation]|[star]|[digits]|[alpha])*
43
+ VALID_PHONE_NUMBER_ = '[' + PLUS_CHARS_ + ']*(?:[' +
44
+ VALID_PUNCTUATION + STAR_SIGN + ']*[' +
45
+ VALID_DIGITS_ + ']){3,}[' +
46
+ VALID_PUNCTUATION +
47
+ STAR_SIGN +
48
+ VALID_ALPHA_ +
49
+ VALID_DIGITS_ + ']*'
50
+
51
+ VALID_PHONE_NUMBER_PATTERN_ = Regexp.compile('\A' + VALID_PHONE_NUMBER_ + '(?:' + KNOWN_EXTN_PATTERNS_ + ')?' + '\z', 'i')
52
+
53
+
54
+ MAX_LENGTH_COUNTRY_CODE_ = 3
55
+ UNKNOWN_REGION_ = 'ZZ'
56
+ # The minimum and maximum length of the national significant number.
57
+ MIN_LENGTH_FOR_NSN_ = 3
58
+ # The ITU says the maximum length should be 15, but we have found longer numbers in Germany.
59
+ MAX_LENGTH_FOR_NSN_ = 16
60
+
61
+
62
+ DIGIT_MAPPINGS = {
63
+ '0' => '0',
64
+ '1' => '1',
65
+ '2' => '2',
66
+ '3' => '3',
67
+ '4' => '4',
68
+ '5' => '5',
69
+ '6' => '6',
70
+ '7' => '7',
71
+ '8' => '8',
72
+ '9' => '9' #,
73
+
74
+ # TODO : wait for ruby 1.9 to use unicode
75
+
76
+ # '\uFF10' => '0', # Fullwidth digit 0
77
+ # '\uFF11' => '1', # Fullwidth digit 1
78
+ # '\uFF12' => '2', # Fullwidth digit 2
79
+ # '\uFF13' => '3', # Fullwidth digit 3
80
+ # '\uFF14' => '4', # Fullwidth digit 4
81
+ # '\uFF15' => '5', # Fullwidth digit 5
82
+ # '\uFF16' => '6', # Fullwidth digit 6
83
+ # '\uFF17' => '7', # Fullwidth digit 7
84
+ # '\uFF18' => '8', # Fullwidth digit 8
85
+ # '\uFF19' => '9', # Fullwidth digit 9
86
+ # '\u0660' => '0', # Arabic-indic digit 0
87
+ # '\u0661' => '1', # Arabic-indic digit 1
88
+ # '\u0662' => '2', # Arabic-indic digit 2
89
+ # '\u0663' => '3', # Arabic-indic digit 3
90
+ # '\u0664' => '4', # Arabic-indic digit 4
91
+ # '\u0665' => '5', # Arabic-indic digit 5
92
+ # '\u0666' => '6', # Arabic-indic digit 6
93
+ # '\u0667' => '7', # Arabic-indic digit 7
94
+ # '\u0668' => '8', # Arabic-indic digit 8
95
+ # '\u0669' => '9', # Arabic-indic digit 9
96
+ # '\u06F0' => '0', # Eastern-Arabic digit 0
97
+ # '\u06F1' => '1', # Eastern-Arabic digit 1
98
+ # '\u06F2' => '2', # Eastern-Arabic digit 2
99
+ # '\u06F3' => '3', # Eastern-Arabic digit 3
100
+ # '\u06F4' => '4', # Eastern-Arabic digit 4
101
+ # '\u06F5' => '5', # Eastern-Arabic digit 5
102
+ # '\u06F6' => '6', # Eastern-Arabic digit 6
103
+ # '\u06F7' => '7', # Eastern-Arabic digit 7
104
+ # '\u06F8' => '8', # Eastern-Arabic digit 8
105
+ # '\u06F9' => '9' # Eastern-Arabic digit 9
106
+ }
107
+
108
+ class << self
109
+
110
+ def parse(numberToParse, alpha2)
111
+
112
+ return self.parseHelper_(numberToParse, alpha2.upcase, false, true)
113
+
114
+ end
115
+
116
+ def parseAndKeepRawInput(numberToParse, alpha2)
117
+
118
+ if !isValidRegionCode_(alpha2)
119
+ if (numberToParse.length > 0 && numberToParse[0..0] != PLUS_SIGN)
120
+ raise "INVALID_COUNTRY_CODE"
121
+ end
122
+ end
123
+
124
+ return self.parseHelper_(numberToParse, alpha2.upcase, true, true)
125
+
126
+ end
127
+
128
+
129
+ def parseHelper_(numberToParse, defaultRegion, keepRawInput, checkRegion)
130
+
131
+ if numberToParse.blank?
132
+ raise "NOT_A_NUMBER"
133
+ end
134
+
135
+ nationalNumber = extractPossibleNumber(numberToParse)
136
+
137
+ if !isViablePhoneNumber(nationalNumber)
138
+ raise "NOT_A_NUMBER"
139
+ end
140
+
141
+ if checkRegion && !checkRegionForParsing_(nationalNumber, defaultRegion)
142
+ raise "INVALID_COUNTRY_CODE"
143
+ end
144
+
145
+ # build PhoneNumber instance
146
+ phoneNumber = I18nPhoneNumbers::PhoneNumber.new
147
+
148
+ if keepRawInput
149
+ phoneNumber.raw_input = numberToParse
150
+ end
151
+
152
+ # Attempt to parse extension first, since it doesn't require
153
+ # country-specific data and we want to have the non-normalised number here.
154
+ #
155
+ # phoneNumber.extension = self.maybeStripExtension(nationalNumber)
156
+
157
+
158
+ regionMetadata = I18nPhoneNumbers::Metadata.countryToMetadata[defaultRegion]
159
+
160
+ # Check to see if the number is given in international format so we know
161
+ # whether this number is from the default country or not.
162
+ normalizedNationalNumber = ''
163
+
164
+ countryCode = maybeExtractCountryCode(nationalNumber, regionMetadata, normalizedNationalNumber, keepRawInput, phoneNumber)
165
+
166
+
167
+ if countryCode != 0
168
+
169
+ phoneNumberRegion = getRegionCodeForCountryCode(countryCode)
170
+
171
+ if phoneNumberRegion != defaultRegion
172
+ regionMetadata = getMetadataForRegion(phoneNumberRegion)
173
+ end
174
+
175
+ else
176
+ # If no extracted country code, use the region supplied instead. The
177
+ # national number is just the normalized version of the number we were
178
+ # given to parse
179
+ normalizeSB_(nationalNumber)
180
+
181
+ normalizedNationalNumber.replace(nationalNumber.dup)
182
+
183
+ if !defaultRegion.nil?
184
+ countryCode = regionMetadata.country_code
185
+ phoneNumber.country_code = countryCode
186
+ elsif keepRawInput
187
+ phoneNumber.country_code_source = nil
188
+ end
189
+
190
+ end
191
+
192
+ if normalizedNationalNumber.length < MIN_LENGTH_FOR_NSN_
193
+ raise "TOO_SHORT_NSN"
194
+ end
195
+
196
+ if !regionMetadata.nil?
197
+
198
+ carrierCode = ''
199
+
200
+ maybeStripNationalPrefixAndCarrierCode(normalizedNationalNumber, regionMetadata, carrierCode)
201
+
202
+ if keepRawInput
203
+ phoneNumber.preferred_domestic_carrier_code = carrierCode
204
+ end
205
+
206
+ end
207
+
208
+ normalizedNationalNumberStr = normalizedNationalNumber.dup
209
+
210
+ lengthOfNationalNumber = normalizedNationalNumberStr.length
211
+
212
+
213
+ raise "TOO_SHORT_NSN" if lengthOfNationalNumber < MIN_LENGTH_FOR_NSN_
214
+
215
+ raise "TOO_LONG" if lengthOfNationalNumber > MAX_LENGTH_FOR_NSN_
216
+
217
+ if normalizedNationalNumberStr[0..0] == '0' && regionMetadata.leading_zero_possible
218
+ phoneNumber.italian_leading_zero = true
219
+ end
220
+
221
+ phoneNumber.national_number = normalizedNationalNumberStr.to_i # to_i, really ?
222
+
223
+ return phoneNumber
224
+
225
+ end
226
+
227
+
228
+
229
+ # Checks to see that the region code used is valid, or if it is not valid, that the number to
230
+ # parse starts with a + symbol so that we can attempt to infer the region from the number.
231
+ # Returns false if it cannot use the region provided and the region cannot be inferred.
232
+ def checkRegionForParsing_(numberToParse, defaultRegion)
233
+
234
+ return isValidRegionCode_(defaultRegion) ||
235
+ (!numberToParse.nil? && numberToParse.length > 0 && numberToParse.match(LEADING_PLUS_CHARS_PATTERN_))
236
+
237
+ end
238
+
239
+
240
+ # Tries to extract a country code from a number. This method will return zero
241
+ # if no country code is considered to be present. Country codes are extracted
242
+ # in the following ways:
243
+ # - by stripping the international dialing prefix of the country the person is
244
+ # dialing from, if this is present in the number, and looking at the next
245
+ # digits
246
+ # - by stripping the '+' sign if present and then looking at the next digits
247
+ # - by comparing the start of the number and the country code of the default
248
+ # region. If the number is not considered possible for the numbering plan of
249
+ # the default region initially, but starts with the country code of this
250
+ # region, validation will be reattempted after stripping this country code. If
251
+ # this number is considered a possible number, then the first digits will be
252
+ # considered the country code and removed as such.
253
+ #
254
+ # It will throw a i18n.phonenumbers.Error if the number starts with a '+' but
255
+ # the country code supplied after this does not match that of any known
256
+ # country.
257
+ #
258
+ # @param {string} number non-normalized telephone number that we wish to
259
+ # extract a country code from - may begin with '+'.
260
+ # @param {i18n.phonenumbers.PhoneMetadata} defaultRegionMetadata metadata
261
+ # about the region this number may be from.
262
+ # @param {!goog.string.StringBuffer} nationalNumber a string buffer to store
263
+ # the national significant number in, in the case that a country code was
264
+ # extracted. The number is appended to any existing contents. If no country
265
+ # code was extracted, this will be left unchanged.
266
+ # @param {boolean} keepRawInput true if the country_code_source and
267
+ # preferred_carrier_code fields of phoneNumber should be populated.
268
+ # of phoneNumber should be populated.
269
+ # @param {i18n.phonenumbers.PhoneNumber} phoneNumber the PhoneNumber object
270
+ # that needs to be populated with country code and country code source.
271
+ # Note the country code is always populated, whereas country code source is
272
+ # only populated when keepCountryCodeSource is true.
273
+ # @return {number} the country code extracted or 0 if none could be extracted.
274
+ # @throws {i18n.phonenumbers.Error}
275
+ def maybeExtractCountryCode(number, defaultRegionMetadata, nationalNumber, keepRawInput, phoneNumber)
276
+
277
+ if (number.length == 0)
278
+ return 0
279
+ end
280
+
281
+ fullNumber = number.dup
282
+
283
+ possibleCountryIddPrefix = nil
284
+
285
+ if !defaultRegionMetadata.nil?
286
+ possibleCountryIddPrefix = defaultRegionMetadata.international_prefix
287
+ end
288
+
289
+ if possibleCountryIddPrefix.nil?
290
+ possibleCountryIddPrefix = 'NonMatch' # put something that will NEVER match
291
+ end
292
+
293
+ countryCodeSource = maybeStripInternationalPrefixAndNormalize(fullNumber, possibleCountryIddPrefix)
294
+
295
+ if keepRawInput
296
+ phoneNumber.country_code_source = countryCodeSource
297
+ end
298
+
299
+
300
+ if countryCodeSource != I18nPhoneNumbers::CountryCodeSource::FROM_DEFAULT_COUNTRY
301
+
302
+ if fullNumber.length < MIN_LENGTH_FOR_NSN_
303
+ raise "TOO_SHORT_AFTER_IDD"
304
+ end
305
+
306
+ potentialCountryCode = extractCountryCode(fullNumber, nationalNumber)
307
+
308
+ if potentialCountryCode != 0
309
+ phoneNumber.country_code = potentialCountryCode
310
+ return potentialCountryCode
311
+ end
312
+
313
+ # If this fails, they must be using a strange country code that we don't
314
+ # recognize, or that doesn't exist.
315
+ raise "INVALID_COUNTRY_CODE"
316
+
317
+ elsif !defaultRegionMetadata.nil?
318
+
319
+ # Check to see if the number starts with the country calling code for the
320
+ # default region. If so, we remove the country calling code, and do some
321
+ # checks on the validity of the number before and after.
322
+ defaultCountryCode = defaultRegionMetadata.country_code
323
+
324
+ defaultCountryCodeString = defaultCountryCode.to_s
325
+
326
+ normalizedNumber = fullNumber.dup
327
+
328
+ if normalizedNumber.match(Regexp.new('\A' + defaultCountryCodeString))
329
+
330
+ potentialNationalNumber = normalizedNumber[(defaultCountryCodeString.length)..-1]
331
+
332
+ generalDesc = defaultRegionMetadata.general_desc
333
+
334
+ validNumberPattern = Regexp.new(generalDesc.national_number_pattern)
335
+
336
+ maybeStripNationalPrefixAndCarrierCode(potentialNationalNumber, defaultRegionMetadata, nil)
337
+
338
+ potentialNationalNumberStr = potentialNationalNumber.dup
339
+
340
+ possibleNumberPattern = generalDesc.possible_number_pattern
341
+
342
+ # If the number was not valid before but is valid now, or if it was too
343
+ # long before, we consider the number with the country calling code
344
+ # stripped to be a better result and keep that instead.
345
+ if (!matchesEntirely_(validNumberPattern, fullNumber) && matchesEntirely_(validNumberPattern, potentialNationalNumberStr)) ||
346
+ (testNumberLengthAgainstPattern_(possibleNumberPattern, fullNumber) == "TOO_LONG")
347
+
348
+ nationalNumber = nationalNumber.replace(potentialNationalNumberStr)
349
+
350
+ if keepRawInput
351
+ phoneNumber.country_code_source = I18nPhoneNumbers::CountryCodeSource::FROM_NUMBER_WITHOUT_PLUS_SIGN
352
+ end
353
+
354
+ phoneNumber.country_code = defaultCountryCode
355
+
356
+ return defaultCountryCode
357
+
358
+ end
359
+ end
360
+
361
+ end
362
+
363
+ # No country code present.
364
+ phoneNumber.country_code = 0
365
+ return 0
366
+
367
+ end
368
+
369
+
370
+ def testNumberLengthAgainstPattern_(numberPattern, number)
371
+
372
+ if matchesEntirely_(numberPattern, number)
373
+ return "IS_POSSIBLE"
374
+ end
375
+
376
+ return number.match('\A' + numberPattern) ? "TOO_LONG" : "TOO_SHORT"
377
+
378
+ end
379
+
380
+
381
+ # Strips any international prefix (such as +, 00, 011) present in the number
382
+ # provided, normalizes the resulting number, and indicates if an international
383
+ # prefix was present.
384
+ #
385
+ # @param {!goog.string.StringBuffer} number the non-normalized telephone number
386
+ # that we wish to strip any international dialing prefix from.
387
+ # @param {string} possibleIddPrefix the international direct dialing prefix
388
+ # from the country we think this number may be dialed in.
389
+ # @return {i18n.phonenumbers.PhoneNumber.CountryCodeSource} the corresponding
390
+ # CountryCodeSource if an international dialing prefix could be removed
391
+ # from the number, otherwise CountryCodeSource::FROM_DEFAULT_COUNTRY if
392
+ # the number did not seem to be in international format.
393
+ def maybeStripInternationalPrefixAndNormalize(number, possibleIddPrefix)
394
+
395
+ numberStr = number.dup
396
+
397
+ if numberStr.length == 0
398
+ return I18nPhoneNumbers::CountryCodeSource::FROM_DEFAULT_COUNTRY
399
+ end
400
+
401
+ # Check to see if the number begins with one or more plus signs.
402
+ if numberStr.match(LEADING_PLUS_CHARS_PATTERN_)
403
+
404
+ numberStr = numberStr.sub(LEADING_PLUS_CHARS_PATTERN_, '') # strip leading +
405
+
406
+ # Can now normalize the rest of the number since we've consumed the "+"
407
+ # sign at the start.
408
+ number.replace(normalize(numberStr))
409
+ return I18nPhoneNumbers::CountryCodeSource::FROM_NUMBER_WITH_PLUS_SIGN
410
+
411
+ end
412
+
413
+ # Attempt to parse the first digits as an international prefix.
414
+ iddPattern = Regexp.new(possibleIddPrefix)
415
+
416
+ normalizeSB_(number)
417
+
418
+ return parsePrefixAsIdd_(iddPattern, number) ?
419
+ I18nPhoneNumbers::CountryCodeSource::FROM_NUMBER_WITH_IDD :
420
+ I18nPhoneNumbers::CountryCodeSource::FROM_DEFAULT_COUNTRY
421
+
422
+ end
423
+
424
+
425
+ # Strips the IDD from the start of the number if present. Helper function used
426
+ # by maybeStripInternationalPrefixAndNormalize.
427
+ #
428
+ # @param {RegExp} iddPattern the regular expression for the international
429
+ # prefix.
430
+ # @param {!goog.string.StringBuffer} number the phone number that we wish to
431
+ # strip any international dialing prefix from.
432
+ # @return {boolean} true if an international prefix was present.
433
+ # @private
434
+ def parsePrefixAsIdd_(iddPattern, number)
435
+
436
+ if number.index(iddPattern) == 0
437
+
438
+ matchEnd = number.match(iddPattern)[0].length
439
+
440
+ matchedGroups = number[matchEnd..-1].match(CAPTURING_DIGIT_PATTERN)
441
+
442
+ if matchedGroups && matchedGroups[1] == '0'
443
+ return false
444
+ end
445
+
446
+ number.replace(number[matchEnd..-1])
447
+ return true
448
+ end
449
+
450
+ return false
451
+
452
+ end
453
+
454
+
455
+ # Normalizes a string of characters representing a phone number. This is a
456
+ # wrapper for normalize(String number) but does in-place normalization of the
457
+ # StringBuffer provided.
458
+ #
459
+ # @param {!goog.string.StringBuffer} number a StringBuffer of characters
460
+ # representing a phone number that will be normalized in place.
461
+ # @private
462
+ def normalizeSB_(number)
463
+
464
+ normalizedNumber = normalize(number)
465
+ number.replace(normalizedNumber)
466
+
467
+ end
468
+
469
+
470
+
471
+ # Normalizes a string of characters representing a phone number. This performs
472
+ # the following conversions:
473
+ # - Wide-ascii digits are converted to normal ASCII (European) digits.
474
+ # - Punctuation is stripped.
475
+ # - Arabic-Indic numerals are converted to European numerals.
476
+ #
477
+ # @param {string} number a string of characters representing a phone number.
478
+ # @return {string} the normalized string version of the phone number.
479
+ def normalize(number)
480
+ return normalizeHelper_(number, DIGIT_MAPPINGS, true)
481
+ end
482
+
483
+
484
+ # Normalizes a string of characters representing a phone number by replacing
485
+ # all characters found in the accompanying map with the values therein, and
486
+ # stripping all other characters if removeNonMatches is true.
487
+ #
488
+ # @param {string} number a string of characters representing a phone number.
489
+ # @param {!Object} normalizationReplacements a mapping of characters to what
490
+ # they should be replaced by in the normalized version of the phone number.
491
+ # @param {boolean} removeNonMatches indicates whether characters that are not
492
+ # able to be replaced should be stripped from the number. If this is false,
493
+ # they will be left unchanged in the number.
494
+ # @return {string} the normalized string version of the phone number.
495
+ # @private
496
+ def normalizeHelper_(number, normalizationReplacements = DIGIT_MAPPINGS, removeNonMatches = true)
497
+
498
+ normalizedNumber = ''
499
+
500
+ number.each_char do |character|
501
+
502
+ newDigit = normalizationReplacements[character.upcase()]
503
+
504
+ if !newDigit.nil?
505
+ normalizedNumber << newDigit
506
+ elsif !removeNonMatches
507
+ normalizedNumber << character
508
+ end
509
+ # If neither of the above are true, we remove this character.
510
+ end
511
+
512
+ return normalizedNumber
513
+ end
514
+
515
+
516
+ # Check whether the entire input sequence can be matched against the regular
517
+ # expression.
518
+ #
519
+ # @param {RegExp|string} regex the regular expression to match against.
520
+ # @param {string} str the string to test.
521
+ # @return {boolean} true if str can be matched entirely against regex.
522
+ # @private
523
+ def matchesEntirely_(regex, str)
524
+
525
+ matchedGroups = str.match(regex) # regex is a string, match will correctly convert it into a regex first
526
+
527
+ return !!(matchedGroups && matchedGroups[0].length == str.length)
528
+
529
+ end
530
+
531
+
532
+ def extractCountryCode(fullNumber, nationalNumber)
533
+
534
+ [fullNumber.length, MAX_LENGTH_COUNTRY_CODE_].min.times do |i|
535
+
536
+ potentialCountryCode = fullNumber[0..i].to_i
537
+
538
+ if I18nPhoneNumbers::Metadata.countryCodeToRegionCodeMap.has_key?(potentialCountryCode)
539
+ nationalNumber.replace(fullNumber[(i+1)..-1])
540
+ return potentialCountryCode
541
+ end
542
+
543
+ end
544
+
545
+ return 0
546
+
547
+ end
548
+
549
+
550
+ # Strips any national prefix (such as 0, 1) present in the number provided.
551
+ #
552
+ # @param {!goog.string.StringBuffer} number the normalized telephone number
553
+ # that we wish to strip any national dialing prefix from.
554
+ # @param {i18n.phonenumbers.PhoneMetadata} metadata the metadata for the
555
+ # country that we think this number is from.
556
+ # @return {string} the carrier code extracted if it is present, otherwise
557
+ # return an empty string.
558
+ def maybeStripNationalPrefixAndCarrierCode(number, metadata, carrierCode)
559
+
560
+ numberStr = number.dup
561
+
562
+ possibleNationalPrefix = metadata.national_prefix_for_parsing
563
+
564
+ if numberStr == '' || possibleNationalPrefix.blank?
565
+ # Early return for numbers of zero length.
566
+ return false
567
+ end
568
+
569
+ # Attempt to parse the first digits as a national prefix.
570
+ prefixPattern = Regexp.new('\A(?:' + possibleNationalPrefix + ')')
571
+
572
+ prefixMatcher = numberStr.match(prefixPattern) # nil or MatchData instance (same behavior as Array)
573
+
574
+ if (prefixMatcher)
575
+
576
+ nationalNumberRule = Regexp.new(metadata.general_desc.national_number_pattern)
577
+
578
+ # prefixMatcher[numOfGroups] == nil implies nothing was captured by the
579
+ # capturing groups in possibleNationalPrefix; therefore, no transformation
580
+ # is necessary, and we just remove the national prefix.
581
+ numOfGroups = prefixMatcher.length - 1
582
+
583
+ transformRule = metadata.national_prefix_transform_rule
584
+
585
+ noTransform = transformRule.blank? || prefixMatcher[numOfGroups].nil? || prefixMatcher[numOfGroups].length == 0
586
+
587
+ # Juste remove the national prefix
588
+ if noTransform
589
+ transformedNumber = numberStr[(prefixMatcher[0].length)..-1]
590
+
591
+ # Apply the transformRule
592
+ else
593
+ transformedNumber = numberStr.sub(prefixPattern, transformRule)
594
+ end
595
+
596
+ # If the original number was viable, and the resultant number is not,
597
+ # we return.
598
+ if matchesEntirely_(nationalNumberRule, numberStr) && !matchesEntirely_(nationalNumberRule, transformedNumber)
599
+ return false
600
+ end
601
+
602
+ if (noTransform && numOfGroups > 0 && !prefixMatcher[1].nil?) ||
603
+ (!noTransform && numOfGroups > 1)
604
+
605
+ if !carrierCode.nil?
606
+ carrierCode += prefixMatcher[1]
607
+ end
608
+ end
609
+
610
+ number.replace(transformedNumber)
611
+ return true
612
+ end
613
+
614
+ return false
615
+
616
+ end
617
+
618
+
619
+ # Returns the region code that matches the specific country code. In the case
620
+ # of no region code being found, ZZ will be returned. In the case of multiple
621
+ # regions, the one designated in the metadata as the "main" country for this
622
+ # calling code will be returned.
623
+ #
624
+ # @param {number} countryCode the country calling code.
625
+ # @return {string}
626
+ def getRegionCodeForCountryCode(countryCode)
627
+
628
+ regionCodes = I18nPhoneNumbers::Metadata::countryCodeToRegionCodeMap[countryCode]
629
+
630
+ return regionCodes.nil? ? UNKNOWN_REGION_ : regionCodes[0]
631
+
632
+ end
633
+
634
+
635
+ # @param {?string} regionCode
636
+ # @return {i18n.phonenumbers.PhoneMetadata}
637
+ def getMetadataForRegion(regionCode)
638
+
639
+ return regionCode.nil? ? nil : I18nPhoneNumbers::Metadata::countryToMetadata[regionCode.upcase]
640
+
641
+ end
642
+
643
+
644
+ # Gets the type of a phone number.
645
+ #
646
+ # @param {i18n.phonenumbers.PhoneNumber} number the phone number that we want
647
+ # to know the type.
648
+ # @return {i18n.phonenumbers.PhoneNumberType} the type of the phone number.
649
+ def getNumberType(number)
650
+
651
+ regionCode = getRegionCodeForNumber(number)
652
+
653
+ if !isValidRegionCode_(regionCode) && REGION_CODE_FOR_NON_GEO_ENTITY != regionCode
654
+ return I18nPhoneNumbers::PhoneNumberType::UNKNOWN
655
+ end
656
+
657
+ nationalSignificantNumber = getNationalSignificantNumber(number)
658
+
659
+ return getNumberTypeHelper_(nationalSignificantNumber, getMetadataForRegion(regionCode))
660
+
661
+ end
662
+
663
+
664
+ # Gets the type of a phone number.
665
+ #
666
+ # @param {i18n.phonenumbers.PhoneNumber} number the phone number that we want
667
+ # to know the type.
668
+ # @return {boolean}
669
+ def isMobile?(number)
670
+ return [I18nPhoneNumbers::PhoneNumberType::MOBILE,
671
+ I18nPhoneNumbers::PhoneNumberType::FIXED_LINE_OR_MOBILE].include?(getNumberType(number))
672
+ end
673
+
674
+
675
+ # Returns the country/region where a phone number is from. This could be used
676
+ # for geo-coding in the country/region level.
677
+ #
678
+ # @param {i18n.phonenumbers.PhoneNumber} number the phone number whose origin
679
+ # we want to know.
680
+ # @return {?string} the country/region where the phone number is from, or null
681
+ # if no country matches this calling code.
682
+ def getRegionCodeForNumber(number)
683
+
684
+ return nil if number.nil?
685
+
686
+ countryCode = number.country_code || 0
687
+
688
+ regions = I18nPhoneNumbers::Metadata::countryCodeToRegionCodeMap[countryCode]
689
+
690
+ return nil if regions.nil?
691
+
692
+ if regions.length == 1
693
+ return regions[0]
694
+ else
695
+ return getRegionCodeForNumberFromRegionList_(number, regions)
696
+ end
697
+
698
+ end
699
+
700
+
701
+ # @param {i18n.phonenumbers.PhoneNumber} number
702
+ # @param {Array.<string>} regionCodes
703
+ # @return {?string}
704
+ # @private
705
+ def getRegionCodeForNumberFromRegionList_(number, regionCodes)
706
+
707
+ nationalNumber = number.national_number.to_s
708
+
709
+ regionCodes.each { |regionCode|
710
+
711
+ # If leadingDigits is present, use this. Otherwise, do full validation.
712
+ metadata = getMetadataForRegion(regionCode)
713
+
714
+ if !metadata.leading_digits.blank?
715
+
716
+ return regionCode if !!nationalNumber.match(Regexp.compile('\A' + metadata.leading_digits)) # starts with leading_digits pattern
717
+
718
+ elsif getNumberTypeHelper_(nationalNumber, metadata) != I18nPhoneNumbers::PhoneNumberType::UNKNOWN
719
+
720
+ return regionCode
721
+
722
+ end
723
+
724
+ }
725
+
726
+ return nil
727
+
728
+ end
729
+
730
+
731
+ # @param {string} nationalNumber
732
+ # @param {i18n.phonenumbers.PhoneMetadata} metadata
733
+ # @return {i18n.phonenumbers.PhoneNumberType}
734
+ # @private
735
+ def getNumberTypeHelper_(nationalNumber, metadata)
736
+
737
+ generalNumberDesc = metadata.general_desc
738
+
739
+ if generalNumberDesc.national_number_pattern.nil? || !isNumberMatchingDesc_(nationalNumber, generalNumberDesc)
740
+ return I18nPhoneNumbers::PhoneNumberType::UNKNOWN
741
+ end
742
+
743
+ # TODO : init and use types different from MOBILE and FIXED
744
+
745
+ # if isNumberMatchingDesc_(nationalNumber, metadata.premium_rate)
746
+ # return I18nPhoneNumbers::PhoneNumberType::PREMIUM_RATE
747
+ # end
748
+ #
749
+ # if isNumberMatchingDesc_(nationalNumber, metadata.toll_free)
750
+ # return I18nPhoneNumbers::PhoneNumberType::TOLL_FREE
751
+ # end
752
+ #
753
+ # if isNumberMatchingDesc_(nationalNumber, metadata.shared_cost)
754
+ # return I18nPhoneNumbers::PhoneNumberType::SHARED_COST
755
+ # end
756
+ #
757
+ # if isNumberMatchingDesc_(nationalNumber, metadata.voip)
758
+ # return I18nPhoneNumbers::PhoneNumberType::VOIP
759
+ # end
760
+ #
761
+ # if isNumberMatchingDesc_(nationalNumber, metadata.personal_number)
762
+ # return I18nPhoneNumbers::PhoneNumberType::PERSONAL_NUMBER
763
+ # end
764
+ #
765
+ # if isNumberMatchingDesc_(nationalNumber, metadata.pager)
766
+ # return I18nPhoneNumbers::PhoneNumberType::PAGER
767
+ # end
768
+ #
769
+ # if isNumberMatchingDesc_(nationalNumber, metadata.uan)
770
+ # return I18nPhoneNumbers::PhoneNumberType::UAN
771
+ # end
772
+ #
773
+ # if isNumberMatchingDesc_(nationalNumber, metadata.voicemail)
774
+ # return I18nPhoneNumbers::PhoneNumberType::VOICEMAIL
775
+ # end
776
+
777
+ isFixedLine = isNumberMatchingDesc_(nationalNumber, metadata.fixed_line)
778
+
779
+ if isFixedLine
780
+
781
+ if metadata.same_mobile_and_fixed_line_pattern
782
+ return I18nPhoneNumbers::PhoneNumberType::FIXED_LINE_OR_MOBILE
783
+
784
+ elsif isNumberMatchingDesc_(nationalNumber, metadata.mobile)
785
+ return I18nPhoneNumbers::PhoneNumberType::FIXED_LINE_OR_MOBILE
786
+ end
787
+
788
+ return I18nPhoneNumbers::PhoneNumberType::FIXED_LINE
789
+
790
+ end
791
+
792
+ # Otherwise, test to see if the number is mobile. Only do this if certain
793
+ # that the patterns for mobile and fixed line aren't the same.
794
+ if !metadata.same_mobile_and_fixed_line_pattern && isNumberMatchingDesc_(nationalNumber, metadata.mobile)
795
+ return I18nPhoneNumbers::PhoneNumberType::MOBILE
796
+ end
797
+
798
+ return I18nPhoneNumbers::PhoneNumberType::UNKNOWN
799
+ end
800
+
801
+
802
+ # @param {string} nationalNumber
803
+ # @param {i18n.phonenumbers.PhoneNumberDesc} numberDesc
804
+ # @return {boolean}
805
+ # @private
806
+ def isNumberMatchingDesc_(nationalNumber, numberDesc)
807
+
808
+ return matchesEntirely_(numberDesc.possible_number_pattern, nationalNumber) &&
809
+ matchesEntirely_(numberDesc.national_number_pattern, nationalNumber)
810
+
811
+ end
812
+
813
+
814
+ # Helper function to check region code is not unknown or null.
815
+ #
816
+ # @param {?string} regionCode the ISO 3166-1 two-letter country code that
817
+ # denotes the country/region that we want to get the country code for.
818
+ # @return {boolean} true if region code is valid.
819
+ # @private
820
+ def isValidRegionCode_(regionCode)
821
+
822
+ return !regionCode.nil? &&
823
+ regionCode != REGION_CODE_FOR_NON_GEO_ENTITY &&
824
+ I18nPhoneNumbers::Metadata::countryToMetadata.has_key?(regionCode.upcase)
825
+
826
+ end
827
+
828
+
829
+ # Gets the national significant number of the a phone number. Note a national
830
+ # significant number doesn't contain a national prefix or any formatting.
831
+ #
832
+ # @param {i18n.phonenumbers.PhoneNumber} number the PhoneNumber object for
833
+ # which the national significant number is needed.
834
+ # @return {string} the national significant number of the PhoneNumber object
835
+ # passed in.
836
+ def getNationalSignificantNumber(number)
837
+
838
+ # If a leading zero has been set, we prefix this now. Note this is not a
839
+ # national prefix.
840
+ nationalNumber = number.national_number.to_s
841
+
842
+ if number.italian_leading_zero
843
+ return '0' + nationalNumber
844
+ end
845
+
846
+ return nationalNumber
847
+
848
+ end
849
+
850
+
851
+ # Attempts to extract a possible number from the string passed in. This
852
+ # currently strips all leading characters that could not be used to start a
853
+ # phone number. Characters that can be used to start a phone number are defined
854
+ # in the VALID_START_CHAR_PATTERN. If none of these characters are found in the
855
+ # number passed in, an empty string is returned. This function also attempts to
856
+ # strip off any alternative extensions or endings if two or more are present,
857
+ # such as in the case of: (530) 583-6985 x302/x2303. The second extension here
858
+ # makes this actually two phone numbers, (530) 583-6985 x302 and (530) 583-6985
859
+ # x2303. We remove the second extension so that the first number is parsed
860
+ # correctly.
861
+ #
862
+ # @param {string} number the string that might contain a phone number.
863
+ # @return {string} the number, stripped of any non-phone-number prefix (such as
864
+ # 'Tel:') or an empty string if no character used to start phone numbers
865
+ # (such as + or any digit) is found in the number.
866
+ #
867
+ def extractPossibleNumber(number)
868
+
869
+ start = number.index(VALID_START_CHAR_PATTERN)
870
+
871
+ if !start.nil?
872
+
873
+ possibleNumber = number[start..-1]
874
+
875
+ # Remove trailing non-alpha non-numerical characters.
876
+ possibleNumber = possibleNumber.gsub(UNWANTED_END_CHAR_PATTERN_, '')
877
+
878
+ # Check for extra numbers at the end.
879
+ secondNumberStart = possibleNumber.index(SECOND_NUMBER_START_PATTERN_)
880
+
881
+ if !secondNumberStart.nil?
882
+ possibleNumber = possibleNumber[0..secondNumberStart]
883
+ end
884
+
885
+ else
886
+ possibleNumber = ''
887
+ end
888
+
889
+ return possibleNumber
890
+
891
+ end
892
+
893
+
894
+ # Checks to see if the string of characters could possibly be a phone number at
895
+ # all. At the moment, checks to see that the string begins with at least 3
896
+ # digits, ignoring any punctuation commonly found in phone numbers. This method
897
+ # does not require the number to be normalized in advance - but does assume
898
+ # that leading non-number symbols have been removed, such as by the method
899
+ # extractPossibleNumber.
900
+ #
901
+ # @param {string} number string to be checked for viability as a phone number.
902
+ # @return {boolean} true if the number could be a phone number of some sort,
903
+ # otherwise false.
904
+ def isViablePhoneNumber(number)
905
+
906
+ return false if (number.length < MIN_LENGTH_FOR_NSN_)
907
+
908
+ return matchesEntirely_(VALID_PHONE_NUMBER_PATTERN_, number)
909
+
910
+ end
911
+
912
+
913
+ # Tests whether a phone number matches a valid pattern. Note this doesn't
914
+ # verify the number is actually in use, which is impossible to tell by just
915
+ # looking at a number itself.
916
+ #
917
+ # @param {i18n.phonenumbers.PhoneNumber} number the phone number that we want
918
+ # to validate.
919
+ # @return {boolean} a boolean that indicates whether the number is of a valid
920
+ # pattern.
921
+ def isValidNumber(number)
922
+
923
+ regionCode = getRegionCodeForNumber(number)
924
+
925
+ return isValidRegionCode_(regionCode) && isValidNumberForRegion(number, regionCode)
926
+ end
927
+
928
+
929
+ # Tests whether a phone number is valid for a certain region. Note this doesn't
930
+ # verify the number is actually in use, which is impossible to tell by just
931
+ # looking at a number itself. If the country code is not the same as the
932
+ # country code for the region, this immediately exits with false. After this,
933
+ # the specific number pattern rules for the region are examined. This is useful
934
+ # for determining for example whether a particular number is valid for Canada,
935
+ # rather than just a valid NANPA number.
936
+ #
937
+ # @param {i18n.phonenumbers.PhoneNumber} number the phone number that we want
938
+ # to validate.
939
+ # @param {string} regionCode the ISO 3166-1 two-letter country code that
940
+ # denotes the region/country that we want to validate the phone number for.
941
+ # @return {boolean} a boolean that indicates whether the number is of a valid
942
+ # pattern.
943
+ def isValidNumberForRegion(number, regionCode)
944
+
945
+ return false if (number.country_code || 0) != getCountryCodeForRegion(regionCode)
946
+
947
+ metadata = getMetadataForRegion(regionCode)
948
+
949
+ generalNumDesc = metadata.general_desc
950
+
951
+ nationalSignificantNumber = getNationalSignificantNumber(number)
952
+
953
+ # For countries where we don't have metadata for PhoneNumberDesc, we treat
954
+ # any number passed in as a valid number if its national significant number
955
+ # is between the minimum and maximum lengths defined by ITU for a national
956
+ # significant number.
957
+ if generalNumDesc.national_number_pattern.blank?
958
+
959
+ numberLength = nationalSignificantNumber.length
960
+
961
+ return numberLength > MIN_LENGTH_FOR_NSN_ && numberLength <= MAX_LENGTH_FOR_NSN_
962
+ end
963
+
964
+ return getNumberTypeHelper_(nationalSignificantNumber, metadata) != I18nPhoneNumbers::PhoneNumberType::UNKNOWN
965
+ end
966
+
967
+
968
+ # Returns the country calling code for a specific region. For example, this
969
+ # would be 1 for the United States, and 64 for New Zealand.
970
+ #
971
+ # @param {?string} regionCode the ISO 3166-1 two-letter country code that
972
+ # denotes the country/region that we want to get the country code for.
973
+ # @return {number} the country calling code for the country/region denoted by
974
+ # regionCode.
975
+ def getCountryCodeForRegion(regionCode)
976
+
977
+ return 0 if !isValidRegionCode_(regionCode)
978
+
979
+ metadata = getMetadataForRegion(regionCode)
980
+ return 0 if metadata == nil
981
+
982
+ return metadata.country_code || 0
983
+ end
984
+
985
+
986
+
987
+ # Formats a phone number in the specified format using default rules. Note that
988
+ # this does not promise to produce a phone number that the user can dial from
989
+ # where they are - although we do format in either 'national' or
990
+ # 'international' format depending on what the client asks for, we do not
991
+ # currently support a more abbreviated format, such as for users in the same
992
+ # "area" who could potentially dial the number without area code. Note that if
993
+ # the phone number has a country code of 0 or an otherwise invalid country
994
+ # code, we cannot work out which formatting rules to apply so we return the
995
+ # national significant number with no formatting applied.
996
+ #
997
+ # @param {i18n.phonenumbers.PhoneNumber} number the phone number to be
998
+ # formatted.
999
+ # @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the
1000
+ # phone number should be formatted into.
1001
+ # @return {string} the formatted phone number.
1002
+ def format(number, numberFormat)
1003
+
1004
+ countryCallingCode = number.country_code || 0
1005
+
1006
+ nationalSignificantNumber = getNationalSignificantNumber(number)
1007
+
1008
+ if (numberFormat == I18nPhoneNumbers::PhoneNumberFormat::E164)
1009
+
1010
+ # Early exit for E164 case since no formatting of the national number needs
1011
+ # to be applied. Extensions are not formatted.
1012
+ return formatNumberByFormat_(countryCallingCode, I18nPhoneNumbers::PhoneNumberFormat::E164, nationalSignificantNumber, '')
1013
+
1014
+ end
1015
+
1016
+ # Note getRegionCodeForCountryCode() is used because formatting information
1017
+ # for countries which share a country code is contained by only one country
1018
+ # for performance reasons. For example, for NANPA countries it will be
1019
+ # contained in the metadata for US.
1020
+ regionCode = getRegionCodeForCountryCode(countryCallingCode)
1021
+
1022
+ return nationalSignificantNumber if !isValidRegionCode_(regionCode)
1023
+
1024
+ metadata = getMetadataForRegion(regionCode)
1025
+
1026
+ formattedExtension = maybeGetFormattedExtension_(number, regionCode)
1027
+
1028
+ formattedNationalNumber = formatNationalNumber_(nationalSignificantNumber, metadata, numberFormat)
1029
+
1030
+ return formatNumberByFormat_(countryCallingCode, numberFormat, formattedNationalNumber, formattedExtension)
1031
+
1032
+ end
1033
+
1034
+
1035
+ # A helper function that is used by format and formatByPattern.
1036
+ #
1037
+ # @param {number} countryCode the country calling code.
1038
+ # @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the
1039
+ # phone number should be formatted into.
1040
+ # @param {string} formattedNationalNumber
1041
+ # @param {string} formattedExtension
1042
+ # @return {string} the formatted phone number.
1043
+ # @private
1044
+ def formatNumberByFormat_(countryCode, numberFormat, formattedNationalNumber, formattedExtension)
1045
+
1046
+ pnf = I18nPhoneNumbers::PhoneNumberFormat
1047
+
1048
+ res = case numberFormat
1049
+ when pnf::E164 then PLUS_SIGN + countryCode.to_s + formattedNationalNumber + formattedExtension
1050
+ when pnf::INTERNATIONAL then PLUS_SIGN + countryCode.to_s + ' ' + formattedNationalNumber + formattedExtension
1051
+ # "else" is including pnf::NATIONAL
1052
+ else formattedNationalNumber + formattedExtension
1053
+ end
1054
+
1055
+ return res
1056
+ end
1057
+
1058
+
1059
+
1060
+ # Gets the formatted extension of a phone number, if the phone number had an
1061
+ # extension specified. If not, it returns an empty string.
1062
+ #
1063
+ # @param {i18n.phonenumbers.PhoneNumber} number the PhoneNumber that might have
1064
+ # an extension.
1065
+ # @param {string} regionCode the ISO 3166-1 two-letter country code.
1066
+ # @return {string} the formatted extension if any.
1067
+ # @private
1068
+ def maybeGetFormattedExtension_(number, regionCode)
1069
+
1070
+ # NOT SUPPORTED YET
1071
+ return ''
1072
+
1073
+ # if number.extension.blank?
1074
+ # return ''
1075
+ # else
1076
+ # return formatExtension_(number.getExtensionOrDefault(), regionCode)
1077
+ # end
1078
+
1079
+ end
1080
+
1081
+
1082
+ # Note in some countries, the national number can be written in two completely
1083
+ # different ways depending on whether it forms part of the NATIONAL format or
1084
+ # INTERNATIONAL format. The numberFormat parameter here is used to specify
1085
+ # which format to use for those cases. If a carrierCode is specified, this will
1086
+ # be inserted into the formatted string to replace $CC.
1087
+ #
1088
+ # @param {string} number a string of characters representing a phone number.
1089
+ # @param {i18n.phonenumbers.PhoneMetadata} metadata the metadata for the
1090
+ # region that we think this number is from.
1091
+ # @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the
1092
+ # phone number should be formatted into.
1093
+ # @param {string=} opt_carrierCode
1094
+ # @return {string} the formatted phone number.
1095
+ # @private
1096
+ def formatNationalNumber_(number, metadata, numberFormat, opt_carrierCode = nil)
1097
+
1098
+ intlNumberFormats = metadata.intl_number_formats
1099
+
1100
+ # When the intlNumberFormats exists, we use that to format national number
1101
+ # for the INTERNATIONAL format instead of using the numberDesc.numberFormats.
1102
+ if (intlNumberFormats.length == 0 || numberFormat == I18nPhoneNumbers::PhoneNumberFormat::NATIONAL)
1103
+ availableFormats = metadata.number_formats
1104
+ else
1105
+ availableFormats = metadata.intl_number_formats
1106
+ end
1107
+
1108
+ return formatAccordingToFormats_(number, availableFormats, numberFormat, opt_carrierCode)
1109
+ end
1110
+
1111
+
1112
+ # Note that carrierCode is optional - if nil or an empty string, no carrier
1113
+ # code replacement will take place.
1114
+ #
1115
+ # @param {string} nationalNumber a string of characters representing a phone
1116
+ # number.
1117
+ # @param {Array.<i18n.phonenumbers.NumberFormat>} availableFormats the
1118
+ # available formats the phone number could be formatted into.
1119
+ # @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the
1120
+ # phone number should be formatted into.
1121
+ # @param {string=} opt_carrierCode
1122
+ # @return {string} the formatted phone number.
1123
+ # @private
1124
+ def formatAccordingToFormats_(nationalNumber, availableFormats, numberFormat, opt_carrierCode = nil)
1125
+
1126
+ availableFormats.each do |numFormat|
1127
+
1128
+ ldp = numFormat.leading_digits_patterns
1129
+
1130
+ # We always use the last leading_digits_pattern, as it is the most detailed.
1131
+ if ldp.empty? || nationalNumber.index(Regexp.compile(ldp[-1])) == 0
1132
+
1133
+ patternToMatch = Regexp.new(numFormat.pattern)
1134
+
1135
+ if matchesEntirely_(patternToMatch, nationalNumber)
1136
+
1137
+ numberFormatRule = numFormat.format
1138
+
1139
+ domesticCarrierCodeFormattingRule = numFormat.carrier_code_formatting_rule
1140
+
1141
+ if (numberFormat == I18nPhoneNumbers::PhoneNumberFormat::NATIONAL &&
1142
+ !opt_carrierCode.blank? &&
1143
+ !domesticCarrierCodeFormattingRule.blank?)
1144
+
1145
+ # Replace the $CC in the formatting rule with the desired carrier code.
1146
+ carrierCodeFormattingRule = domesticCarrierCodeFormattingRule.sub(CC_PATTERN_, opt_carrierCode)
1147
+
1148
+ # Now replace the $FG in the formatting rule with the first group
1149
+ # and the carrier code combined in the appropriate way.
1150
+ numberFormatRule = numberFormatRule.sub(FIRST_GROUP_PATTERN_, carrierCodeFormattingRule)
1151
+
1152
+ return nationalNumber.sub(patternToMatch, numberFormatRule)
1153
+
1154
+ else
1155
+ # Use the national prefix formatting rule instead.
1156
+ nationalPrefixFormattingRule = numFormat.national_prefix_formatting_rule
1157
+
1158
+ if (numberFormat == I18nPhoneNumbers::PhoneNumberFormat::NATIONAL &&
1159
+ !nationalPrefixFormattingRule.blank?)
1160
+ return nationalNumber.sub(patternToMatch, numberFormatRule.sub(FIRST_GROUP_PATTERN_, nationalPrefixFormattingRule))
1161
+ else
1162
+ return nationalNumber.sub(patternToMatch, numberFormatRule)
1163
+ end
1164
+ end
1165
+ end
1166
+ end
1167
+ end
1168
+
1169
+ # If no pattern above is matched, we format the number as a whole.
1170
+ return nationalNumber
1171
+ end
1172
+
1173
+
1174
+
1175
+ # Gets a valid number for the specified country.
1176
+ #
1177
+ # @param {string} regionCode the ISO 3166-1 two-letter country code that
1178
+ # denotes the country for which an example number is needed.
1179
+ # @return {i18n.phonenumbers.PhoneNumber} a valid fixed-line number for the
1180
+ # specified country. Returns null when the metadata does not contain such
1181
+ # information.
1182
+ def getExampleNumber(regionCode)
1183
+ return getExampleNumberForType(regionCode, I18nPhoneNumbers::PhoneNumberType::FIXED_LINE)
1184
+ end
1185
+
1186
+
1187
+ # Gets a valid number, if any, for the specified country and number type.
1188
+ #
1189
+ # @param {string} regionCode the ISO 3166-1 two-letter country code that
1190
+ # denotes the country for which an example number is needed.
1191
+ # @param {i18n.phonenumbers.PhoneNumberType} type the type of number that is
1192
+ # needed.
1193
+ # @return {i18n.phonenumbers.PhoneNumber} a valid number for the specified
1194
+ # country and type. Returns null when the metadata does not contain such
1195
+ # information.
1196
+ def getExampleNumberForType(regionCode, type)
1197
+
1198
+ desc = getNumberDescByType_(getMetadataForRegion(regionCode), type)
1199
+
1200
+ begin
1201
+ if !desc.example_number.blank?
1202
+ return parse(desc.example_number, regionCode)
1203
+ end
1204
+ rescue
1205
+ end
1206
+
1207
+ return nil
1208
+ end
1209
+
1210
+
1211
+ #
1212
+ # @param {i18n.phonenumbers.PhoneMetadata} metadata
1213
+ # @param {i18n.phonenumbers.PhoneNumberType} type
1214
+ # @return {i18n.phonenumbers.PhoneNumberDesc}
1215
+ # @private
1216
+ def getNumberDescByType_(metadata, type)
1217
+
1218
+ case type
1219
+ # when I18nPhoneNumbers::PhoneNumberType::PREMIUM_RATE then metadata.premium_rate
1220
+ # when I18nPhoneNumbers::PhoneNumberType::TOLL_FREE then metadata.toll_free
1221
+ when I18nPhoneNumbers::PhoneNumberType::MOBILE then metadata.mobile
1222
+ when I18nPhoneNumbers::PhoneNumberType::FIXED_LINE, I18nPhoneNumbers::PhoneNumberType::FIXED_LINE_OR_MOBILE then metadata.fixed_line
1223
+ # when I18nPhoneNumbers::PhoneNumberType::SHARED_COST then metadata.shared_cost
1224
+ # when I18nPhoneNumbers::PhoneNumberType::VOIP then metadata.voip
1225
+ # when I18nPhoneNumbers::PhoneNumberType::PERSONAL_NUMBER then metadata.personal_number
1226
+ # when I18nPhoneNumbers::PhoneNumberType::PAGER then metadata.pager
1227
+ # when I18nPhoneNumbers::PhoneNumberType::UAN then metadata.uan
1228
+ else metadata.general_desc
1229
+ end
1230
+
1231
+ end
1232
+
1233
+ end # end of class << self
1234
+
1235
+ end # end of Util module
1236
+
1237
+ end