Open-source project
floere/phony avatar
floere/phony

phony: splitting, normalizing and formatting E.164 numbers in Ruby

E164 international phone number normalizing, splitting, formatting.

1,093 stars236 forksRubyMIT

At a glance

What is it?
phony is a Ruby gem for international phone numbers, with four operations that cover most of what an application needs: normalize a number for storage, format it for display, judge whether it is plausible, and split it into country code, national destination code and local part. It deliberately handles international numbers only, and it can be trimmed to load just the country codes you care about.
Who is it for?
Adopt phony if your Ruby application stores or displays international phone numbers and you want the country code, national destination code and local part separated rather than a string with punctuation stripped. Do not adopt it for national-only data or for validation of local formats, because the README states plainly that Phony works with international numbers only and that a valid E164 number must include a country code.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 64 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Numbers are treated as country code, national code and local part

Phony normalizes, formats and splits E164 phone numbers, and a valid E164 number must include a country code. The README opens with a disclaimer that is really a scope statement: Phony works with international numbers only, such as `61 412 345 678`. That is a design decision rather than a gap, because the whole value of the library is in knowing the numbering plan of a country, and a national number without a country code cannot be attributed to one. The anatomy is spelled out with the Australian example. The country code is `61`, the national destination code is the `4` that marks a mobile number in Australia, and the local number part is the remaining `12 345 678`. Once you have that decomposition, three questions become answerable: which country this is, whether the local prefix is plausible for that country, and how to lay the number out for a reader. The README lists the countries it handles, and calls that list mildly unmaintained, which is a candid admission that coverage is the weak point rather than the algorithms.

Four operations, and the exact shape of their answers

The API is small enough to read in one screen, and the README's examples are precise about return types, which is where this kind of library usually gets subtle. Normalize removes all non-numeric characters and is the operation to use before storing a number in a database. Format takes a normalized number and options, and formats it according to a country's predominant formatting, with lots of options for international, national and local formatting. Plausible asks whether a number is plausible, which is a sanity check rather than a validation. Split returns the parts as an array of country code, national destination code and local part, and the README includes the cases where the shape changes. For the number `3928061371` the array is `['39', '2', '806', '1371']`, and for Denmark, which has no national destination code, the NDC position is `false` rather than a string, giving `['45', false, '12', '12', '12', '12']`. That `false` in the middle of the array is the kind of detail that saves an hour of debugging later, and it is documented rather than left to be discovered.

The country rules are the library, and they are what you can trim

Most of the memory Phony uses is spent on the country code rules, and the README quantifies it: according to memory_profiler, the gem uses roughly 1MB of memory per Ruby process, and the measurement command is given so you can reproduce it. From Phony 2.18.0 there is a way to pay less, and it is the feature that makes the gem usable in a memory constrained service. You require the config file, `require 'phony/config'`, and then load only the country codes you need with `Phony::Config.load(only: ['41', '44'])`, or everything except some with `Phony::Config.load(except: ['41', '44'])`, or the convenience form `Phony::Config.load('41', '44')`. The README repeats that each of these loads the rest of Phony, and gives its own measurement, where loading only the NANP country code retains around 63kB. The difference between 1MB and 63kB per process is not dramatic in a single web server, but it is decisive in a setup with many small processes, which is the situation the feature was written for.

Documentation and functional tests are the same artefact

Phony uses qed as its documentation system and to run its functional tests, and the README's usage section points at the usage index in `qed/index.md` with a separate page per operation, so `qed/normalize.md`, `qed/format.md`, `qed/plausibility.md`, `qed/split.md` and `qed/config.md` each hold the documentation and the assertions for that behaviour. The examples in the README are written in that style, with the assertion inline, which is why you can read a page and then run it. The test suite is described as roughly 2,250 tests that run in 2 seconds, a figure given as of April 2019, and the repository layout backs the claim up with a `spec/` directory, an Rakefile, a `.rspec` file and a `.rubocop.yml`, plus a separate `performance/` directory for the benchmarks and a `misc/` directory. For a library whose correctness is entirely a question of numbering plans, having the expected outputs written next to the prose is the right way to keep the two in step.

Installing it, and the Rails path

Three install routes are given. With Bundler, you append `gem 'phony'` to your Gemfile and run bundle install. Without Bundler, you run `gem install phony` from the command line. And if you are on Rails, the README sends you to a separate project, phony_rails, which is the framework integration rather than the library. That separation is a useful signal about how the library is meant to be consumed: the core library does not carry a framework, so the Rails integration can stay separate from it. The homepage points at the author's own page for the gem, and the repository includes `HISTORY.md` and a `RELEASING.md` document, which tells you that releases are cut deliberately rather than pushed from a CI run.

What it is not, and what to check before you depend on it

The boundaries are all in the README, but they are easy to skim. Phony does not validate national numbers, and it does not tell you whether a number is assigned, only whether it is plausible. The country list is the other boundary: it is long, covering the United States, the United Kingdom, Germany, France, Australia, New Zealand, Brazil, India, Japan, South Korea, Russia and many more, and the README describes it as mildly unmaintained, which means a new numbering plan change may not be reflected. The sponsorship line shows the shape of the project too, since the README lists one sponsor, Renuo AG, with a period from July 2022 to January 2025. Against that, the library itself is stable, MIT licensed, and its interface has been stable enough that a Rails integration, a documentation site and a performance directory all exist around it. The sensible adoption pattern is to use it for storage and display of international numbers, keep your own validation layer for anything stronger, and watch the country list for the markets you care about.

Editorial conclusion

Adopt phony if your Ruby application stores or displays international phone numbers and you want the country code, national destination code and local part separated rather than a string with punctuation stripped. Do not adopt it for national-only data or for validation of local formats, because the README states plainly that Phony works with international numbers only and that a valid E164 number must include a country code. Before you adopt it, check that the countries you care about are in the handled list, which the README itself calls mildly unmaintained, and decide whether you want the full country data or a trimmed load, since the full set costs about 1MB of retained memory per Ruby process while loading a single country code such as the NANP one is around 63kB. The licence is MIT, the repository publishes no GitHub releases, and the last push was on 2026-08-20.

Frequently asked questions

What does Phony.split return?

The number split into its parts, the country code, the national destination code and the local part. For a country without an NDC, such as Denmark, the NDC position is false, so `Phony.split('4512121212')` gives `['45', false, '12', '12', '12', '12']`.

How do I reduce the memory Phony uses?

From Phony 2.18.0 you can load only the country codes you need with `require 'phony/config'` followed by `Phony::Config.load(only: ['41', '44'])`, or exclude some with `Phony::Config.load(except: ['41', '44'])`. The full set costs roughly 1MB per Ruby process, while loading only the NANP country code retains about 63kB.

Does Phony work with local or national phone numbers?

No. The README states that Phony works with international numbers only and that a valid E164 number must include a country code, giving `61 412 345 678` as the example format.

How do I install and use Phony in a Rails application?

The README points Rails users at a separate project, phony_rails, for the framework integration. The gem itself installs with `gem install phony` or by adding `gem 'phony'` to your Gemfile and running bundle install.

Where is the Phony documentation?

Phony uses qed for its documentation and for its functional tests, and the usage index is in `qed/index.md`, with a separate page per operation such as `qed/normalize.md`, `qed/format.md`, `qed/plausibility.md`, `qed/split.md` and `qed/config.md`.

Official sources

  1. floere/phony on GitHub
  2. Issues
  3. License: MIT
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/floere-phony.svg)](https://hysenlabs.com/projects/floere-phony)

Was this useful?