Telephone numbers: country and number in one input group, checked against that country's numbering plan, shown grouped its way #304

Closed
opened 2026-09-29 11:26:37 +00:00 by tiagoagueda · 1 comment
Owner

From the interface review of 2026-09-29:

  • The country prefix and the number become one Basecoat input group.
  • A number is validated against the country chosen before it is stored. France is the example: 06 12 34 56 78 (the national form) and 6 12 34 56 78 (without the trunk zero) are both valid, and both are +33 6 12 34 56 78.
  • Digits are grouped for reading (pairs, in France), and the grouping is shown, not typed.
  • The rules differ from country to country.

What is there today

  • core/phone_field.py is a country select and a text box side by side (partials/phone_widget.html), with the flag beside the select.
  • core/phones.py::combine already does the first half of the France example. A national number gets the country's code in front with its leading zeros removed, so 06 12 34 56 78 and 6 12 34 56 78 under France both become +33612345678. A number that starts with + or 00 ignores the select.
  • It validates nothing, deliberately. The module's docstring says that deciding whether a number is real "needs the whole numbering plan of every country, which is a multi-megabyte library and a constant stream of updates", and that a number nobody can parse is stored exactly as it was typed, because refusing to save it "would be the worst possible outcome".
  • phones.readable groups the digits in threes whatever the country, for the same reason: a country's own grouping is part of the plan the module does not carry.

Decisions to take

  1. Reversing that decision. What was asked for needs the numbering plans. The standard source is Google's libphonenumber metadata, available in Python as phonenumbers (Apache 2.0), and as a phonenumberslite build without the geocoding and carrier data. It is a dependency with regular releases, which dependencies.yml would pick up every month.
  2. Refuse, or warn. "Validated before storing" can mean refusing a number the plan says cannot exist, or saving it with a warning. The docstring's argument was about numbers nobody can parse, and the plan can now tell those apart from numbers that are merely unusual. Proposal: refuse a number that is impossible for its country (the wrong length, a prefix the country does not use), and keep one the plan cannot place, with a warning beside it.
  3. Numbers already stored that the new check would refuse stay as they are, marked, and are checked the next time they are saved. Nothing is rewritten silently.
  4. Where the grouping is drawn. Everywhere a number is shown (the page, a contact, a CV, the export), and always from the stored E.164 form, so the number typed and the number kept stay the same number.

Done when

  • The input group is one control to a screen reader, with the country select and the box each labelled and the flag inside the group. It reflows at 320 pixels: the reason phone_numbers.html gives for the number having a line of its own still holds.
  • Tests cover the France cases above, a number with +, a number with 00, a number too short for its country, and a country with no trunk prefix.
  • phones.py's docstring says what it does now and why the decision changed.
From the interface review of 2026-09-29: - The country prefix and the number become one Basecoat input group. - A number is validated against the country chosen before it is stored. France is the example: `06 12 34 56 78` (the national form) and `6 12 34 56 78` (without the trunk zero) are both valid, and both are `+33 6 12 34 56 78`. - Digits are grouped for reading (pairs, in France), and the grouping is shown, not typed. - The rules differ from country to country. ## What is there today - `core/phone_field.py` is a country select and a text box side by side (`partials/phone_widget.html`), with the flag beside the select. - `core/phones.py::combine` already does the first half of the France example. A national number gets the country's code in front with its leading zeros removed, so `06 12 34 56 78` and `6 12 34 56 78` under France both become `+33612345678`. A number that starts with `+` or `00` ignores the select. - **It validates nothing, deliberately.** The module's docstring says that deciding whether a number is real "needs the whole numbering plan of every country, which is a multi-megabyte library and a constant stream of updates", and that a number nobody can parse is stored exactly as it was typed, because refusing to save it "would be the worst possible outcome". - `phones.readable` groups the digits in threes whatever the country, for the same reason: a country's own grouping is part of the plan the module does not carry. ## Decisions to take 1. **Reversing that decision.** What was asked for needs the numbering plans. The standard source is Google's libphonenumber metadata, available in Python as `phonenumbers` (Apache 2.0), and as a `phonenumberslite` build without the geocoding and carrier data. It is a dependency with regular releases, which `dependencies.yml` would pick up every month. 2. **Refuse, or warn.** "Validated before storing" can mean refusing a number the plan says cannot exist, or saving it with a warning. The docstring's argument was about numbers nobody can parse, and the plan can now tell those apart from numbers that are merely unusual. Proposal: refuse a number that is impossible for its country (the wrong length, a prefix the country does not use), and keep one the plan cannot place, with a warning beside it. 3. **Numbers already stored** that the new check would refuse stay as they are, marked, and are checked the next time they are saved. Nothing is rewritten silently. 4. **Where the grouping is drawn.** Everywhere a number is shown (the page, a contact, a CV, the export), and always from the stored E.164 form, so the number typed and the number kept stay the same number. ## Done when - The input group is one control to a screen reader, with the country select and the box each labelled and the flag inside the group. It reflows at 320 pixels: the reason `phone_numbers.html` gives for the number having a line of its own still holds. - Tests cover the France cases above, a number with `+`, a number with `00`, a number too short for its country, and a country with no trunk prefix. - `phones.py`'s docstring says what it does now and why the decision changed.
tiagoagueda added this to the 0.5.0 milestone 2026-09-29 11:26:37 +00:00
Author
Owner

Decided (2026-09-29): add the numbering plans (phonenumberslite) and refuse what is impossible. A number that cannot exist for its country (the wrong length, a prefix the country does not use) is refused with the reason. A number the plan cannot place is kept, with a warning beside it. Numbers already stored are marked, never rewritten, and checked again the next time they are saved. Grouping follows each country's own rules, drawn from the stored E.164 form wherever a number is shown.

**Decided (2026-09-29): add the numbering plans (`phonenumberslite`) and refuse what is impossible.** A number that cannot exist for its country (the wrong length, a prefix the country does not use) is refused with the reason. A number the plan cannot place is kept, with a warning beside it. Numbers already stored are marked, never rewritten, and checked again the next time they are saved. Grouping follows each country's own rules, drawn from the stored E.164 form wherever a number is shown.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#304
No description provided.