Architecture
spokenform.prepare() applies explicit stages in this order:
validate the selected language and protected ranges;
discover caller-protected ranges and auto-detected literals such as URLs, email addresses, and (when not claimed semantically) semantic versions;
normalize_literals=Trueopts high-confidence URL, e-mail, version, and contextual Roman rendering into structured ownership while caller spans remain absolute;obtain or validate optional lexical annotations;
replace protected ranges with internal sentinels and remap annotation offsets;
normalize Unicode independently when enabled;
recognize complete high-confidence structured sequences, then ask
abbr2wordsfor source-aligned structured quantity identities and realize them with the selected locale’s semantic grammar;expand lexical abbreviations with exact
abbr2wordsreplacements;verbalize remaining generic numeric forms according to
NumberPolicy;when requested, filter residual Unicode punctuation/symbol characters under the caller’s
symbol_mode, while protected sentinels remain opaque;normalize whitespace according to independently configurable controls;
restore protected text and compose stage offset maps.
Each stage records its input, output, edits, and mapped edits. Structured and
abbreviation stages emit exact replacements; temporary text-only stages retain
deterministic diff edits only at stage scope. Semantic rule, domain, and optional
evidence metadata are carried with mapped and composed source replacements for
diagnostics. The final PreparedText.offset_map composes all stage maps from
clean_text coordinates to spoken_text coordinates. PreparedText.source_edits
contains composed SourceReplacement records in original-source/final-output
coordinates; it must not be confused with stage-local mapped_edits.
All public source offsets refer to the original string passed to prepare().
Final offsets refer to PreparedText.spoken_text. Boundary APIs expose explicit
left/right bias for insertions, deletions, and generated replacement text.
Ownership boundary
spokenform owns plain-text normalization, protection, provenance, and coordinate
mapping. Callers own language selection, markup parsing, and mixed-language
segmentation. Downstream G2P systems own tokenization for phoneme generation,
lexicons, pronunciations, and vocabulary IDs.
prepare_language() is the strict application entry point and requires an explicit
language for every call. prepare() retains its English default only for compatibility.
PreparationConfig.for_speech() is the generic TTS-neutral preset; the KokoroG2P
preset and adapter are compatibility conveniences implemented through the same generic
policies.
Annotations supplied to the preparation pipeline are source-aligned input evidence.
Transformations can replace a token, so PreparedText and its mappings do not promise
that source POS, tags, lemmas, or morphology remain valid for generated tokens. A
downstream consumer that needs linguistic analysis must analyze spoken_text afresh.
spokenform owns semantic spacing and punctuation consumed by a structured or
lexical expression. Downstream G2P owns quote style, dash canonicalization,
apostrophe variants, and punctuation choices required only by a model tokenizer.
The optional symbols stage is a caller-requested final residual-output policy,
not semantic recognition. It is disabled for symbol_mode="none"; semantic
punctuation is consumed first by structured recognizers, and model-specific
punctuation remains downstream unless the caller explicitly requests filtering.
The structured boundary is deliberately split by locale: abbr2words recognizes
numeric symbols and returns the exact span, numeric lexeme, category, and canonical
identity; spokenform.structured dispatches to locale-owned semantic grammar.
No symbol or alias inventory is copied into spokenform. French owns its dates,
times, ordinals, decimal digit reading, quantities, temperatures, and currency
decomposition. German owns validated dates and times, conservative contextual years
through the shared render_year() policy, reviewed Euro major/minor realization
with explicit Cent labels, exact excess-fraction preservation, and lexical-boundary
validation. German ordinary abbreviations remain supplied by abbr2words, while
German currencies without reviewed minor grammar use a safe exact decimal fallback
or fail closed. Spanish, Italian, Portuguese, and Czech own their reviewed dates,
quantities, temperatures, currencies, ordinary numbers, and locale-specific
extensions; caller-managed time boundaries remain documented.
English owns reviewed dates, validated clock times, canonical quantities and
currencies, conservative ordinary numbers, and contextual release labels.
Japanese, Korean, and Chinese use explicit native numeric and sequence
vocabularies, with Chinese routed through cn2an. Swedish owns comma-decimal
numbers, reviewed quantities, temperatures, SEK grammar, and canonical
abbr2words unit identities. Swedish dates, digital times, arbitrary initialisms,
and unreviewed specialist sequence domains remain caller-managed or fail closed.
Vietnamese owns comma-decimal numbers, exact fractional precision, reviewed quantities, temperatures, VND/₫ currency, and canonical abbr2words unit identities. Numeralform owns generic Vietnamese cardinals, while abbr2words owns reviewed Vietnamese abbreviation, unit, currency recognition, and labels. Vietnamese dates, digital times, ordinals, arbitrary initialisms, and unreviewed specialist sequence domains remain caller-managed or fail closed.
Thai follows the same small-locale architecture: Numeralform owns generic Thai cardinals, abbr2words owns reviewed Thai abbreviation, unit, and baht identities, and Spokenform owns compact digitwise numeric realization and source mapping. Thai calendar protection is applied after abbreviation expansion and before the plain number pass, so structured values -> abbreviation expansion -> plain number pass preserves date, era, and time bodies. Thai unsupported ranges and specialist sequences fail closed without English vocabulary.
Every locale replacement retains exact source spans and composed source/output
mapping. Supported languages never borrow English fallback words merely because a
shared renderer lacks a locale entry. All reviewed quantity and currency symbols
continue to come from abbr2words.
The dependency direction is:
abbr2words ───────────────┐
Numeralform (non-zh) ────┼─> spokenform
cn2an (zh) ──────────────┘
Full-width numeric compatibility forms are folded in a dedicated mapped stage after NFC and only inside numeric-looking spans. Compatibility symbols such as ㈱ remain available to abbr2words; global NFKC is not used.
prepare_for_kokorog2p() is a deterministic one-language adapter. Its profile
preserves run boundaries, honors protected spans fail-closed, and does not perform
language detection, tokenization, G2P, or model-punctuation rewriting.
The downstream migration set currently includes cs, de, fr, es, it,
pt, and en. English is active on the kokorog2p spokenform adapter for
reviewed structured semantics, contextual single-dot release labels, and safe
ordinary-number categories. Years, suffix ordinals, Roman numerals, phone/ID and
arbitrary multi-dot sequences, numeric suffixes, and G2P decisions remain
downstream-owned.
Structured precedence
Caller protection is absolute. Auto-detected literals are a fallback reservation
against partial generic rewrites; a complete semantic recognizer may claim an
auto-literal before it is reserved. Candidate conflict resolution gives priority
to canonical identifiers, then coordinates and formulas, contextual legal,
sports, and address forms, dates and times, contextual sequences,
quantities/currencies/temperatures, specialist music/math forms, and generic
acronym/product candidates. Fractions and date/phone ambiguity are resolved by
semantic candidate priority rather than regex iteration order.
Unclaimed or ambiguous forms remain opaque. The concrete rule-family ordering is
centralized in spokenform.precedence.SequencePriority; recognizers do not rely
on source regex iteration order to resolve time/reference, ISBN/phone,
version/decimal, version/IPv4, or year/identifier conflicts. Every selected candidate is still an
exact Replacement, so precedence does not weaken source/output mapping or
protected-span behavior.
Address numbers use the library’s stable default address policy: ordinary street numbers are rendered cardinally when the full address is recognized, while compact plates, suites, postal codes, and model identifiers retain their category-specific digitwise policies. Contradictory benchmark conventions are reported as data-quality evidence rather than encoded as row-specific rules.
Recognition policy boundary
Structured recognizers first emit candidates annotated with a semantic domain and evidence basis (intrinsic or contextual). spokenform.recognition_policy filters those candidates before SequencePriority overlap resolution. Surface mode admits intrinsic evidence only and treats missing metadata as contextual, while disabled domains suppress their ownership family and reserve overlapping spans against weaker semantic takeover. This keeps interpretation depth, semantic ownership, and rendering settings as separate policy axes. Diagnostics retain suppressed candidates with machine-readable reasons such as context-not-allowed, disabled-domain, and blocked-by-disabled-domain.
Optional Lexhint boundary
Lexhint is an optional, provider-neutral evidence source. Spokenform supports Lexhint 0.1.2 <= x < 0.3.0; Lexhint 0.1.x uses schema-7 artifacts and Lexhint 0.2.x requires separately published schema-8 runtime artifacts. Applications install the desired local dataset with lexhint dataset download <language> --variant runtime and inject the provider; normalization never downloads data.
The production boundary uses exact lexical lookup, authoritative segmentation, and positive semantic-domain corroboration only. Lexhint fuzzy completion, headword matching, and dictionary-definition search remain development/diagnostic capabilities and do not alter automatic recognition.
Semantic segment boundaries
Recognized semantic expressions may consume source punctuation while preserving a generic textual segment boundary when the punctuation carries cadence or grouping semantics. Contextual countdowns therefore render 3-2-1 as three - two - one. The boundary is model-neutral and downstream G2P adapters own model-specific dash canonicalization. Residual symbol_mode filtering does not remove punctuation intentionally emitted by an accepted structured replacement.