ExPhoneNumber

repository·master·Indexed 42 days ago

https://github.com/ex-phone-number/ex_phone_number

An Elixir library for parsing, formatting, and validating international phone numbers, powered by Google's libphonenumber logic. It provides tools to parse raw strings into %ExPhoneNumber.Model.PhoneNumber{} structs, validate them using is_valid_number?/1 and is_possible_number?/1, and format them into standards like E.164. The library also includes metadata for mapping country and region codes and identifying number types such as :mobile, :fixed_line, and :voip.

Tokens
6.4K
Snippets
27
Records
34
Agent score
52%

What's inside ExPhoneNumber

  1. Structure of the ExPhoneNumber.Model.PhoneNumber struct

    master

    When ExPhoneNumber.parse/2 succeeds, it returns a %ExPhoneNumber.Model.PhoneNumber{} struct containing the following fields:

    • country_code: Numeric country code (e.g., 1 for US, 41 for Switzerland).
    • national_number: The national significant number as an integer.
    • extension: Phone extension if present.
    • raw_input: The original input string (may be nil).
  2. Parse, format, and validate phone numbers with ExPhoneNumber

    master

    The ExPhoneNumber module provides the primary interface for handling international phone numbers. You can parse raw strings into structured %ExPhoneNumber.Model.PhoneNumber{} structs, validate them, and format them into various standards like E.164.

    Core Workflow

    1. Parse: Use ExPhoneNumber.parse/2 with a raw number string and a default region code (e.g., "US").
    2. Validate: Check if the parsed number is valid using is_valid_number?/1 or if it is potentially a valid number using is_possible_number?/1.
    3. Format: Convert the struct back into a string using format/2 with a desired format atom (e.g., :e164).
    # 1. Parse a number
    {:ok, phone_number} = ExPhoneNumber.parse("202-456-1111", "US")
    
    # 2. Validate
    ExPhoneNumber.is_valid_number?(phone_number) # true
    ExPhoneNumber.is_possible_number?(phone_number) # true
    
    # 3. Format
    ExPhoneNumber.format(phone_number, :e164) # "+12024561111"
    
    # 4. Get metadata
    ExPhoneNumber.get_number_type(phone_number) # :fixed_line_or_mobile
  3. Workflow: Processing user phone input

    master

    A common pattern for handling user input is to parse the string, validate it, and then format it into E164 for storage.

    def process_phone_input(phone_string, user_region) do
      case ExPhoneNumber.parse(phone_string, user_region) do
        {:ok, phone_number} ->
          if ExPhoneNumber.is_valid_number?(phone_number) do
            {:ok, ExPhoneNumber.format(phone_number, :e164)}
          else
            {:error, "Invalid phone number for region"}
          end
        {:error, reason} ->
          {:error, "Could not parse phone number: #{reason}"}
      end
    end
  4. Workflow: Formatting for display

    master

    To display numbers appropriately, you can parse an E164 string and choose between :national or :international formats based on the user's region.

    def format_for_display(e164_number, user_region) do
      case ExPhoneNumber.parse(e164_number, "") do
        {:ok, phone_number} ->
          # Show national format if same country, international otherwise
          if phone_number.country_code == get_country_code(user_region) do
            ExPhoneNumber.format(phone_number, :national)
          else
            ExPhoneNumber.format(phone_number, :international)
          end
        {:error, _} ->
          e164_number # fallback to original
      end
    end
  5. Format phone numbers with ExPhoneNumber.format/2

    master

    Use ExPhoneNumber.format/2 to convert a phone number struct into a string using specific format atoms.

    Supported format atoms:

    • :e164: International format without spaces (e.g., +41446681800). Best practice for database storage and APIs.
    • :international: International format with spaces (e.g., +41 44 668 18 00).
    • :national: National format (e.g., 044 668 18 00).
    • :rfc3966: RFC3966 URI format (e.g., tel:+41-44-668-18-00).
    ExPhoneNumber.format(phone_number, :e164)
    ExPhoneNumber.format(phone_number, :international)
    ExPhoneNumber.format(phone_number, :national)
    ExPhoneNumber.format(phone_number, :rfc3966)
  6. Get the type of a phone number

    master

    Use ExPhoneNumber.get_number_type(phone_number) to determine the category of the phone number.

    Returns one of the following atoms:

    • :fixed_line
    • :mobile
    • :fixed_line_or_mobile
    • :toll_free
    • :premium_rate
    • :shared_cost
    • :voip
    • :personal_number
    • :pager
    • :uan
    • :voicemail
    • :unknown
    ExPhoneNumber.get_number_type(phone_number)
  7. Validate phone numbers

    master

    ExPhoneNumber provides several ways to check the validity and possibility of a phone number:

    • ExPhoneNumber.is_valid_number?(phone_number): Returns true if the number is valid according to regional rules.
    • ExPhoneNumber.is_possible_number?(phone_number): Returns true if the number is possible (e.g., correct length) but not necessarily valid.
    • ExPhoneNumber.is_possible_number?(string, region): Allows checking possibility of a raw string without parsing it into a struct first.

    Note: A number can be possible without being valid for a specific region.

    # Check if number is valid according to regional rules
    ExPhoneNumber.is_valid_number?(phone_number) # returns boolean
    
    # Check if number is possible (correct length, etc.)
    ExPhoneNumber.is_possible_number?(phone_number) # returns boolean
    
    # Can also check possibility without parsing first
    ExPhoneNumber.is_possible_number?("650 253 0000", "US") # returns boolean
  8. Parse phone numbers with ExPhoneNumber.parse/2

    master

    Use ExPhoneNumber.parse/2 to convert strings into %ExPhoneNumber.Model.PhoneNumber{} structs.

    Important Rules:

    • Never create %ExPhoneNumber.Model.PhoneNumber{} structs manually; always use the parser.
    • Non-E164 numbers: You must provide a region code (ISO 3166-1 alpha-2, e.g., "US", "CH") if the number does not start with a +.
    • E164 numbers: If the number starts with +, the region code can be an empty string "" or nil.

    The function returns {:ok, phone_number} on success or {:error, reason} on failure.

    # Parse with region code (required for non-E164 numbers)
    {:ok, phone_number} = ExPhoneNumber.parse("044 668 18 00", "CH")
    {:ok, phone_number} = ExPhoneNumber.parse("(650) 253-0000", "US")
    
    # Parse E164 numbers (region code can be empty string or nil)
    {:ok, phone_number} = ExPhoneNumber.parse("+41446681800", "")
    {:ok, phone_number} = ExPhoneNumber.parse("+12024561111", nil)
  9. Configure the metadata file location

    master

    The library loads metadata from an XML file. You can override the default metadata path by setting the :ex_phone_number application configuration key :metadata_file.

    By default, it uses resources/PhoneNumberMetadata.xml (or resources/PhoneNumberMetadataForTesting.xml during tests).

    # In your application configuration
    config :ex_phone_number, metadata_file: "path/to/your/custom_metadata.xml"
  10. Parse, validate, and format phone numbers with ExPhoneNumber

    master
    Use ExPhoneNumber.parse/2 to convert a string into a %ExPhoneNumber.Model.PhoneNumber{} struct. You can provide a country code (e.g., `
  11. Troubleshoot phone number validation errors

    master

    When using ExPhoneNumber, validation failures return specific error messages. You can use these strings to identify why a phone number was rejected during parsing or validation processes.

    Common error messages include:

    • Invalid country calling code: The provided IDD (International Direct Dialing) code does not match a known country.
    • The string supplied did not seem to be a phone number: The input format is unrecognizable as a phone number.
    • Phone number too short after IDD: The number is too short once the country code has been identified.
    • The string supplied is too short to be a phone number: The overall input length is below the minimum required for any valid number.
    • The string supplied is too long to be a phone number: The overall input length exceeds the maximum allowed length.
    # Example of error message strings returned by the library:
    ExPhoneNumber.Constants.ErrorMessages.invalid_country_code()
    # => "Invalid country calling code"
    
    ExPhoneNumber.Constants.ErrorMessages.not_a_number()
    # => "The string supplied did not seem to be a phone number"