Lightweight phone number parsing, validation, formatting, and metadata lookup for 36 countries
gem install philiprehberger-phone
Lightweight phone number parsing, validation, formatting, and metadata lookup for 36 countries
Add to your Gemfile:
gem "philiprehberger-phone"
Or install directly:
gem install philiprehberger-phone
require "philiprehberger/phone"
phone = Philiprehberger::Phone.parse("+1 (555) 123-4567")
phone.valid? # => true
phone.country_code # => "1"
phone.national # => "5551234567"
phone.country # => :us
phone.e164 # => "+15551234567"
phone.formatted # => "(555) 123-4567"
phone.international # => "+1 (555) 123-4567"
phone.country_name # => "United States"
phone = Philiprehberger::Phone.parse("020 7946 0958", country: :gb)
phone.country_code # => "44"
phone.country # => :gb
phone.e164 # => "+442079460958"
A national trunk 0 (used by most of Europe, Australia, and others) is dropped
when composing the E.164 number. It is preserved for NANP (+1) and for plans
where a leading 0 is significant.
Skip the explicit PhoneNumber instance when you just need a string:
Philiprehberger::Phone.format("4155551212", format: :e164, country: :us)
# => "+14155551212"
Philiprehberger::Phone.format("4155551212", format: :international, country: :us)
# => "+1 415 555 1212"
Accepted formats: :e164, :national, :international.
Philiprehberger::Phone.valid?("+44 20 7946 0958") # => true
Philiprehberger::Phone.valid?("+1 555") # => false
Phone.extract scans free text and returns the valid numbers as PhoneNumber
instances, deduped by E.164 and in order of first appearance. Dates, long digit
runs, order numbers, decimals, and digits inside words or emails are skipped:
text = "Call (415) 555-1212 or +44 20 7946 0958. Again: 415.555.1212. Due 2026-10-30."
Philiprehberger::Phone.extract(text, country: :us).map(&:e164)
# => ["+14155551212", "+442079460958"]
The country: hint applies to numbers written without a + prefix.
Phone.country_of returns the country symbol for a number (or nil) without raising:
Philiprehberger::Phone.country_of("+15551234567") # => :us
Philiprehberger::Phone.country_of("020 7946 0958", country: :gb) # => :gb
Philiprehberger::Phone.country_of("garbage") # => nil
The shared NANP +1 code is disambiguated between the US and Canada by the
3-digit area code, so Canadian numbers resolve to :ca (and get correct
area-code and carrier lookups):
Philiprehberger::Phone.country_of("+14161234567") # => :ca (Toronto)
Philiprehberger::Phone.country_of("+12125551234") # => :us (New York)
phone = Philiprehberger::Phone.parse("+18001234567")
phone.phone_type # => :toll_free
phone = Philiprehberger::Phone.parse("+447911123456")
phone.phone_type # => :mobile
phone = Philiprehberger::Phone.parse("+19001234567")
phone.phone_type # => :premium
phone = Philiprehberger::Phone.parse("+18001234567")
phone.toll_free? # => true
phone.mobile? # => false
phone = Philiprehberger::Phone.parse("+12125551234")
phone.area_code_info # => { area_code: "212", region: "New York, NY" }
phone = Philiprehberger::Phone.parse("+442079460958")
phone.area_code_info # => { area_code: "20", region: "London" }
Philiprehberger::Phone.vanity_to_digits("1-800-FLOWERS") # => "18003569377"
Philiprehberger::Phone.vanity_to_digits("1-800-COLLECT") # => "18002655328"
Philiprehberger::Phone.valid_shortcode?("12345", country: :us) # => true
Philiprehberger::Phone.valid_shortcode?("123456", country: :us) # => true
Philiprehberger::Phone.valid_shortcode?("1234", country: :us) # => false
Philiprehberger::Phone.valid_shortcode?("12345", country: :br) # => true
Philiprehberger::Phone.valid_shortcode?("1234", country: :kr) # => true
Supported shortcode countries: US, CA, GB, DE, FR, AU, IN, BR, MX, JP, KR, IT, ES.
a = Philiprehberger::Phone.parse("+1 (555) 123-4567")
b = Philiprehberger::Phone.parse("+1-555-123-4567")
a.similar_to?(b) # => true (same E.164: "+15551234567")
a == b # => true
c = Philiprehberger::Phone.parse("+44 20 7946 0958")
a.similar_to?(c) # => false
PhoneNumber also implements eql? and hash (both keyed off E.164), so equal
numbers dedupe in a Set and collapse as Hash keys regardless of formatting:
require "set"
a = Philiprehberger::Phone.parse("+1 (555) 123-4567")
b = Philiprehberger::Phone.parse("+1-555-123-4567")
Set.new([a, b]).size # => 1
{ a => "primary" }[b] # => "primary"
Carrier lookup is supported for US, CA, GB, and DE.
phone = Philiprehberger::Phone::PhoneNumber.new(country_code: "1", national: "2125551234", country: :us)
phone.carrier # => "AT&T"
phone = Philiprehberger::Phone::PhoneNumber.new(country_code: "1", national: "4161234567", country: :ca)
phone.carrier # => "Rogers"
phone = Philiprehberger::Phone.parse("+15551234567")
phone.country_name # => "United States"
phone = Philiprehberger::Phone.parse("+442079460958")
phone.country_name # => "United Kingdom"
phone = Philiprehberger::Phone.parse("+15551234567")
phone.masked # => "+1******4567"
phone.masked(visible: 0) # => "+1**********"
phone.masked(visible: 99) # => "+15551234567"
phone = Philiprehberger::Phone.parse("+14155551212")
phone.rfc3966 # => "tel:+1-415-555-1212"
phone = Philiprehberger::Phone.parse("+15551234567")
phone.to_h
# => { country_code: "1", national: "5551234567", country: :us, ... }
US, CA, GB, DE, FR, AU, JP, IN, BR, MX, ES, IT, NL, BE, CH, AT, SE, NO, DK, FI, PL, PT, IE, RU, CN, KR, SG, NZ, ZA, NG, KE, EG, AR, CL, CO, PE.
Philiprehberger::Phone| Method | Description |
|---|---|
.parse(input, country: nil) | Parse a phone number string into a PhoneNumber |
.valid?(input, country: nil) | Check if a phone number is valid |
.extract(text, country: nil) | Valid PhoneNumbers found in free text, deduped by E.164, in order of appearance |
.country_of(input, country: nil) | Country symbol (or nil) — non-raising convenience |
.vanity_to_digits(input) | Convert vanity letters to digits (e.g. "1-800-FLOWERS") |
.valid_shortcode?(input, country: :us) | Validate an SMS shortcode for a country |
.format(input, format:, country: nil) | Parse and format in one call; format: accepts :e164, :national, or :international |
Philiprehberger::Phone::PhoneNumber| Method | Description |
|---|---|
#valid? | Whether the number matches country length rules |
#country_code | Numeric country calling code (e.g. "1", "44") |
#national | National number digits only |
#e164 | E.164 format ("+15551234567") |
#formatted | Country-specific national format |
#international | International format with country code |
#rfc3966 | RFC 3966 tel: URI ("tel:+1-415-555-1212") |
#country | Country symbol (e.g. :us, :gb) |
#phone_type | Phone type: :mobile, :landline, :toll_free, :premium, :unknown |
#mobile? | Whether the number is a mobile line |
#landline? | Whether the number is a landline |
#toll_free? | Whether the number is toll-free |
#premium? | Whether the number is a premium-rate line |
#area_code_info | Area code metadata { area_code:, region: } for US/CA/GB/DE |
#similar_to?(other) | Whether two numbers have the same E.164 representation |
#eql?(other) / #hash | E.164-keyed equality + hash, so numbers dedupe in a Set / collapse as Hash keys |
#country_name | Human-readable country name (e.g. "United States") |
#carrier | Carrier name based on prefix (US, CA, GB, DE) |
#masked(visible:) | E.164 form with national digits masked as * except the last visible (default 4) |
#to_h | Hash representation with all phone number attributes |
#inspect | Human-readable debug string |
bundle install
bundle exec rspec
bundle exec rubocop
If you find this project useful: