Skip to content

Recover spoken "year" number forms in the reverse-table backend - #9

Merged
jqueguiner merged 1 commit into
mainfrom
fix-spoken-year-forms
Jul 28, 2026
Merged

Recover spoken "year" number forms in the reverse-table backend#9
jqueguiner merged 1 commit into
mainfrom
fix-spoken-year-forms

Conversation

@jqueguiner

Copy link
Copy Markdown
Owner

Problem

The generic reverse-table backend (every locale except en) matches only
num2words' single canonical spelling of a value. Speech, however, routinely
uses the year reading, which is never the canonical form — so words2num
raised cannot parse on:

lang input expected
de neunzehn neunundneunzig 1999
nl negentien zevenennegentig 1997
nl negentienhonderdzevenennegentig 1997
sv nittonhundranittiosju 1997
fr dix-neuf cent quatre-vingt-dix 1990
it mille novecento ottantotto 1988

For a speech-facing library, years are among the most common spoken numbers.

Fix (all in words2num2-core, table-driven, no per-language grammar)

  • hundred_word(lang) — derives the language's hundred morpheme
    (cent / cento / hundert / honderd / hundra) by rendering
    num2words(200..=900) and taking the longest common suffix. Probing the full
    range makes the pre-hundred vowel vary (it quattr-o / se-i / nov-e), so
    the morpheme itself is isolated rather than a unit's trailing vowel.
  • parse_year(lang, text) — recovers L*100 + R from an explicit hundred
    token (fr), a hundred glued into one token (sv/nl), or two 2-digit groups
    (de/nl), resolving every part through the existing reverse-table primitive.
  • additive hundreds in parse_scaled_inner"novecento" (900) + 88,
    covering the vowel elision num2words applies when gluing
    (novecent-ottantotto), which a naive de-space cannot reproduce.
  • de-spaced retry in base_convert for glued canonicals.

Every path is last-resort: it runs only after the whole-string table hit and
parse_scaled both decline, so a canonical number (vingt trois = 23) is
matched first and never reaches the fallback. No canonical round-trip changes.

Tests

  • New rust tests: hundred_morpheme_is_derived, spoken_year_forms,
    year_forms_via_public_entry (end-to-end through words2num).
  • Full rust suite green; the 133 existing python tests still pass.
  • End-to-end wheel check: the 6 cases above now resolve, and canonical /
    scaled / connector round-trips (deux mille dix, soixante-neuf mille huit,
    dos mil veinticuatro, …) are unchanged — 19/19.

The generic (non-en) backend only matched num2words' single canonical
spelling, so common spoken year readings raised "cannot parse":
  - two 2-digit groups: "neunzehn neunundneunzig" (de) = 1999
  - explicit hundred:    "dix-neuf cent quatre-vingt-dix" (fr) = 1990
  - glued hundred:       "nittonhundranittiosju" (sv) = 1997
  - spaced compound that num2words renders glued:
    "mille novecento ottantotto" (it) = 1988

Add, in words2num2-core:
  - hundred_word(lang): derive the hundred morpheme
    (cent/cento/hundert/honderd/hundra) from num2words(200..=900).
  - parse_year(lang, text): recover L*100+R (explicit or glued hundred)
    and two-group year readings through the existing reverse-table
    primitive, so no per-language grammar is needed.
  - additive hundreds in parse_scaled_inner ("novecento" 900 + 88),
    covering the vowel elision num2words applies when gluing.
  - a de-spaced retry in base_convert for glued canonicals.

Every fall-through is last-resort: a canonical number ("vingt trois" =
23) hits the table first and never reaches them. The rust suite (with
new year-form tests) and the 133 python tests pass.
@jqueguiner
jqueguiner merged commit a7c98bf into main Jul 28, 2026
18 checks passed
@jqueguiner
jqueguiner deleted the fix-spoken-year-forms branch July 28, 2026 06:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant