API reference

Preparation

.. py:function:: prepare(text, *, language=’en’, config=None, profile=None, annotations=None, nlp=None, protected_spans=None, lexical_evidence=None, use_spacy=None, spacy_model=None, expand_abbreviations=True, expand_structured=True, normalize_literals=False, expand_numbers=True, normalize_whitespace=True, normalize_unicode=True, strip_outer_whitespace=True, collapse_horizontal_whitespace=True, normalize_line_whitespace=True, collapse_blank_lines=True, number_policy=None, preserve_run_boundaries=False, model_punctuation=False, symbol_mode=’none’, keep_symbols=’’, generic_acronym_mode=’known_only’, generic_acronym_case=’upper’, long_number_mode=’preserve’, registered_acronym_mode=’expand’, context=True, interpretation_mode=InterpretationMode.CONTEXTUAL, disabled_domains=frozenset({}), allowed_domains=None, sequence_fallback_mode=SequenceFallbackMode.PRESERVE, strict=False) :module: spokenform

Convert one-language written text into a readable form intended for speech.

The caller selects the processing language. Language detection, mixed-language segmentation, and markup parsing belong outside spokenform.

Structured values run before lexical abbreviation expansion and generic numbers so each complete expression receives one semantic replacement.

.. py:function:: prepare_language(text, *, language, **kwargs) :module: spokenform

Prepare one explicitly selected language run with generic policy.

Unlike :func:prepare, this future-facing entry point intentionally has no compatibility default for language. The optional config must use the same language so the call cannot silently process a different language.

.. py:function:: prepare_for_kokorog2p(text, language=’en’, *, config=None, profile=None, **kwargs) :module: spokenform

Prepare one language with the kokorog2p-safe profile.

.. py:function:: prepare_for_piperg2p(text, language=’en’, *, config=None, profile=None, **kwargs) :module: spokenform

Prepare one language with the PiperG2P-safe profile.

.. py:function:: normalize_spacing(text, *, normalize_unicode=True, strip_outer_whitespace=True, collapse_horizontal_whitespace=True, normalize_line_whitespace=True, collapse_blank_lines=True, number_policy=None) :module: spokenform

Apply independently configurable Unicode and whitespace policies.

number_policy remains accepted for 0.2.x compatibility. Numeric policy selection belongs to :func:prepare; spacing itself does not consume it.

normalize_unicode, strip_outer_whitespace, collapse_horizontal_whitespace, normalize_line_whitespace, and collapse_blank_lines are independent policy controls. normalize_whitespace=False remains the compatibility switch for skipping the whitespace stage entirely. normalize_literals=True opts into high-confidence URL, e-mail, semantic-version, and contextual Roman rendering; caller-protected spans always take precedence.

Language identifiers and number backends

Canonical runtime identifiers include 49 base families and 18 exact public locale overlays. supported_languages() returns the base families; supported_languages(include_locales=True) returns all 67 public keys. Exact overlays are preserved for abbreviation routing, while unregistered regional forms fall back to their base language. kk is Spokenform’s Kazakh key, maps to kz for abbr2words, and maps to kk for Numeralform.

The installed Numeralform renderer is capability-based. Chinese uses cn2an; Numeralform availability is reported separately from automatic plain-cardinal ownership, so hi, hy, and mn remain valid language inputs while preserving ordinary numeric source until their normalization ownership is reviewed. Reviewed decimal policies, structured locale grammar, sequence spelling, and KokoroG2P profiles are narrower capabilities and are not implied by global language support.

from spokenform import (
    language_support,
    normalize_language,
    resolve_abbr2words_language,
    resolve_numeralform_locale,
    supported_languages,
    supports_language,
)

assert len(supported_languages()) == 49
assert len(supported_languages(include_locales=True)) == 66
assert "nl" in supported_languages()
assert normalize_language("pt-BR") == "pt_BR"
assert normalize_language("fr_FR") == "fr"
assert normalize_language("en-gb") == "en_GB"
assert resolve_abbr2words_language("es-ni") == "es_NI"
assert resolve_numeralform_locale("fr-FR") == "fr"
assert language_support("hi").plain_cardinals is False
assert supports_language("eu") is False

symbol_mode="none" is the backward-compatible default and applies no general residual-symbol filter. symbol_mode="remove" removes Unicode punctuation and symbol characters (P* and S*) left after semantic recognition. With symbol_mode="keep", keep_symbols is an exact-codepoint allowlist; it must not be empty. An allowlist is invalid in none or remove mode. The active filter is recorded as a symbols stage before whitespace normalization and never changes protected spans. Symbol deletions are included in stage and source/output mapping.

generic_acronym_case="upper" is the default. Set it to "lower" to lowercase only generic grapheme-spaced uppercase acronyms; lexical acronyms, preserved terms, known initialisms, identifiers, and mixed-case tokens keep their normal policies.

Configuration policy modes

The policy modes below are passed through the selected language’s abbr2words registry where abbreviation or initialism data is involved. They are available for every language profile, but the exact recognized vocabulary and number grammar remain locale-specific. They are safety controls: conservative modes prefer leaving an ambiguous token unchanged, while expansive modes can create false positives in identifiers, product names, headlines, years, and other uppercase or digit-heavy text.

Generic acronyms

generic_acronym_mode="known_only" is the default. It lets registered or otherwise known initialisms follow their registry policy and leaves unknown uppercase words alone. For example, an unknown TST remains unchanged while a known entry such as ABC can be rendered according to the registry.

"conservative_unknown" additionally spells unknown uppercase initialisms when the context provides abbreviation evidence, reducing accidental changes to ordinary uppercase prose. "spell_unknown" is the broadest mode: eligible unknown uppercase initialisms are spelled even without as much contextual evidence, so it is useful for abbreviation-heavy input but has the highest false-positive risk.

PreparationConfig(language="en", generic_acronym_mode="known_only")
PreparationConfig(
    language="en",
    generic_acronym_mode="spell_unknown",
    generic_acronym_case="upper",
)

For example, with use_spacy=False, prepare("ABC TST", language="en", generic_acronym_mode="conservative_unknown") produces "A B C T S T" in the current English registry, while the default keeps the unknown TST unchanged. generic_acronym_case="lower" changes only generic grapheme-spaced output ("a b c t s t" in that example); it does not lowercase lexical acronyms or registered terms. These modes are delegated to abbr2words’ initialism policy rather than implemented as a second local abbreviation registry.

Registered acronyms

registered_acronym_mode="expand" is the default and uses the registered initialism expansion supplied by abbr2words. Set it to "spell" when the letters of registered initialisms are preferable to their lexical expansion, for example CEO MIT -> C E O M I T in English. This mode has lower lexical interpretation risk but may be less natural for acronyms whose full names are well known. It affects registered entries, not arbitrary unknown uppercase words, and is forwarded to abbr2words as registered_initialism_mode.

PreparationConfig(language="en", registered_acronym_mode="spell")

Long numbers

long_number_mode="preserve" is the default. It leaves long ungrouped digit strings alone when they could be years, identifiers, account numbers, or other downstream-owned sequences. "contextual" verbalizes a long number when quantity evidence makes cardinal reading safe, such as 844361 items, while continuing to protect identifier-like uses. "cardinal" requests cardinal verbalization for eligible long numbers regardless of that contextual evidence; it is useful for prose known to contain quantities but carries a higher risk of rewriting IDs and years. The number grammar and rendered words are selected by the active language, and this policy is independent of abbr2words’ lexical abbreviation registry.

PreparationConfig(language="en", long_number_mode="contextual")

For English, prepare("844361 items", language="en", long_number_mode="preserve") keeps the digits, while "contextual" and "cardinal" produce "eight hundred forty four thousand three hundred sixty one items".

.. py:class:: PreparationConfig(language=’en’, use_spacy=None, spacy_model=None, expand_abbreviations=True, expand_structured=True, normalize_literals=False, expand_numbers=True, normalize_whitespace=True, normalize_unicode=True, strip_outer_whitespace=True, collapse_horizontal_whitespace=True, normalize_line_whitespace=True, collapse_blank_lines=True, number_policy=None, preserve_run_boundaries=False, model_punctuation=False, symbol_mode=’none’, keep_symbols=’’, generic_acronym_mode=’known_only’, generic_acronym_case=’upper’, long_number_mode=’preserve’, registered_acronym_mode=’expand’, interpretation_mode=InterpretationMode.CONTEXTUAL, sequence_fallback_mode=SequenceFallbackMode.PRESERVE, disabled_domains=frozenset({}), allowed_domains=None, context=True, strict=False) :module: spokenform :canonical: spokenform.config.PreparationConfig

Immutable options controlling single-language written-to-spoken preparation.

.. py:method:: PreparationConfig.for_kokorog2p(language) :module: spokenform :classmethod:

  Return a one-language profile safe for kokorog2p adapters.

.. py:method:: PreparationConfig.for_piperg2p(language) :module: spokenform :classmethod:

  Return a one-language profile safe for piperg2p adapters.

.. py:method:: PreparationConfig.for_speech(language) :module: spokenform :classmethod:

  Return the generic one-language speech preparation preset.

Result models

.. py:class:: PreparedText(source_text, clean_text, spoken_text, language, stages=(), mapped_edits=(), source_replacements=(), protected_spans=(), reserved_spans=(), offset_map=None, warnings=()) :module: spokenform :canonical: spokenform.models.PreparedText

Readable spoken text with normalization provenance.

The mapping describes coordinates only. It does not promise that source linguistic annotations remain valid for generated tokens in spoken_text. Downstream POS or morphology analysis must run after preparation.

.. py:property:: PreparedText.changed :module: spokenform :type: bool

  Return whether preparation changed the source text.

.. py:property:: PreparedText.edits :module: spokenform :type: tuple[~spokenform.models.TextEdit, …]

  Return the ordered edits from every stage.

.. py:method:: PreparedText.map_output_span(start, end) :module: spokenform

  Map a final spoken-text span back to original source coordinates.

.. py:method:: PreparedText.map_source_span(start, end) :module: spokenform

  Map an original source span to final spoken-text coordinates.

.. py:method:: PreparedText.render_changes() :module: spokenform

  Render a compact, human-readable stage report.

.. py:property:: PreparedText.replacements :module: spokenform :type: tuple[~spokenform.models.SourceReplacement, …]

  Stable adapter alias for composed source replacements.

.. py:property:: PreparedText.source_edits :module: spokenform :type: tuple[~spokenform.models.SourceReplacement, …]

  Return edits in the documented source-to-final coordinate space.

.. py:property:: PreparedText.stage_report :module: spokenform :type: str

  Return the diagnostic stage report for adapter logging.

.. py:property:: PreparedText.text :module: spokenform :type: str

  Alias for :attr:`spoken_text`.

.. py:method:: PreparedText.to_adapter_dict() :module: spokenform

  Return the stable downstream-adapter result projection.

.. py:method:: PreparedText.to_dict() :module: spokenform

  Return a JSON-serializable representation.

All public source offsets refer to the original string passed to prepare(); final offsets refer to PreparedText.spoken_text; stage-local offsets remain available only under each PreparationStage. PreparedText.to_adapter_dict() is the stable JSON-ready projection for downstream adapters.

prepare_for_kokorog2p() requires an explicit language and performs no language detection, tokenization, G2P, or model-punctuation rewriting. Its default profile preserves run boundary whitespace. Pass caller-owned protected_spans to prevent semantic replacements; partial overlap is handled fail-closed by protecting the complete recognized quantity expression. Use source_replacements and the offset helpers to rebase downstream token and override coordinates.

PreparationConfig.for_speech(language) provides the generic one-language speech preset. It contains no TTS-engine policy. PreparationConfig.for_kokorog2p() and PreparationConfig.for_piperg2p() produce compatibility configurations for their downstream adapters and are not the architectural center of Spokenform.

PreparedText mappings describe source and output coordinates, not a linguistic analysis of generated tokens. When normalization changes a token, source POS, tag, lemma, or morphology must not be reused for the generated text. Run a fresh linguistic analysis after preparation when downstream processing requires it. The preferred application surface is prepare_language() with PreparationConfig.for_speech(language), PreparedText.source_replacements, and PreparedText.offset_map. The KokoroG2P compatibility surface additionally uses PreparationConfig.for_kokorog2p(), prepare_for_kokorog2p(), and NumberPolicy. Lower-level mapping and stage helpers are advanced exports rather than requirements for a normal adapter.

prepare_for_piperg2p() uses the same prepared-text boundary with Spokenform’s reviewed generic number ownership. It performs no language detection, Piper configuration loading, G2P, phonemization, or model-ID encoding. The caller passes PreparedText.spoken_text and keeps the semantic Spokenform language separate from the Piper voice identifier such as en-us. PiperG2P remains a separately installed downstream package.

KokoroG2P retains its legacy handoff numeric policy. PiperG2P consumes already-prepared text and therefore uses the current generic Spokenform numeric ownership policy. Both profiles preserve source replacements, offset mapping, protected spans, and run boundaries.

The stable application-facing surface is prepare_language(), prepare(), prepare_for_kokorog2p(), prepare_for_piperg2p(), prepare_text, PreparationConfig, NumberPolicy, PreparedText, ProtectedSpan, ProtectionError, TokenAnnotation, and __version__. number_policy_for_language() and normalize_numbers() are stable locale-policy helpers. The annotation adapters, spaCy model helpers, structured-stage helpers, StageResult, and mapping/replacement classes and conversion functions are advanced public APIs: they remain exported for compatibility and diagnostics, but downstream integrations should prefer the high-level preparation surface. No exported symbol is removed in 0.2.2.

.. py:class:: PreparationStage(name, before, after, edits=(), mapped_edits=(), reserved=()) :module: spokenform :canonical: spokenform.models.PreparationStage

The before/after text and edits produced by one stage.

.. py:property:: PreparationStage.changed :module: spokenform :type: bool

  Return whether this stage changed the text.

.. py:class:: TextEdit(start, end, source, replacement, stage) :module: spokenform :canonical: spokenform.models.TextEdit

One edit in a normalization stage.

Offsets are relative to the input of that stage. This keeps every edit exact without pretending that all stages still share the original coordinate space.

.. py:class:: MappedEdit(source_start, source_end, output_start, output_end, source, replacement, stage, language=None, kind=’replacement’, rule=None, recognition_domain=None, recognition_evidence=None, evidence_source=None, evidence_score=None, evidence_cues=()) :module: spokenform :canonical: spokenform.models.MappedEdit

A replacement with source and output coordinates.

Coordinates and semantic metadata describe the generated text. Source linguistic annotations are not carried as a valid analysis of replacement text; downstream POS or morphology consumers must analyze the final text.

Annotation adapters

.. py:class:: TokenAnnotation(start, end, text=None, pos=None, tag=None, lemma=None, language=None) :module: spokenform :canonical: spokenform.models.TokenAnnotation

Provider-neutral lexical annotation aligned to one input text.

.. py:function:: annotations_from_spacy(doc) :module: spokenform

Convert a spaCy-like Doc into source-aligned annotations.

The adapter imports no spaCy modules and can therefore be used with compatible providers or simple test doubles.

.. py:function:: spacy_annotations(text, nlp) :module: spokenform

Run an existing spaCy-compatible pipeline and convert its tokens.

.. py:function:: validate_annotations(text, annotations) :module: spokenform

Validate and materialize source-aligned annotations.

Annotation spans must be ordered, non-overlapping, inside text, and match annotation.text when that optional value is supplied.

Number normalization

The Czech normalize_numbers(language="cs") path delegates to the reviewed structured and structured-safe plain-number grammar. It verbalizes ordinary numbers, validated dates, quantities, temperatures, and canonical currencies; colon-time candidates remain unchanged for caller-managed handling.

.. py:function:: normalize_numbers(text, *, language) :module: spokenform

Verbalize common dates, times, currencies, ordinals, and numbers.

URLs, email addresses, and semantic-version-like values are protected. The implementation is intentionally conservative and is an MVP, not a complete locale grammar.

.. py:function:: normalize_structured(text, *, language, protected_ranges=(), promote_literals=False, generic_acronym_mode=’known_only’, generic_acronym_case=’upper’, interpretation_mode=InterpretationMode.CONTEXTUAL, disabled_domains=frozenset({}), allowed_domains=None, evidence=None, trace=None) :module: spokenform

Normalize structured values and return exact semantic provenance.

.. py:function:: iter_structured_replacements(text, *, language, protected_ranges=(), promote_literals=False, generic_acronym_mode=’known_only’, generic_acronym_case=’upper’, interpretation_mode=InterpretationMode.CONTEXTUAL, disabled_domains=frozenset({}), allowed_domains=None, evidence=None, trace=None) :module: spokenform

Return exact, non-overlapping semantic replacements for one language.

Interpretation policy

PreparationConfig and prepare() expose two orthogonal recognition controls:

  • interpretation_mode="contextual" (default) permits reviewed contextual semantic evidence.

  • interpretation_mode="surface" permits only candidates marked with intrinsic evidence and fails closed on missing metadata.

  • disabled_domains={"chemistry", ...} suppresses selected RecognitionDomain families before precedence resolution.

  • allowed_domains={"quantities", ...} permits only selected semantic families and fails closed for candidates without domain metadata. If a domain appears in both sets, configuration raises ValueError.

from spokenform import prepare_language

prepare_language(
    "The sample contains H2O.",
    language="en",
    interpretation_mode="surface",
    disabled_domains={"chemistry"},
)

context remains the legacy abbreviation-context switch. Surface mode clamps its effective abbreviation context off, but context=False under contextual mode does not disable structured recognizers. InterpretationMode, RecognitionDomain, and RecognitionEvidence are exported public types. Policy-suppressed candidates are visible in structured trace diagnostics.

sequence_fallback_mode="preserve" is the compatibility default. Set it to "spell" to render conservative residual sequence-shaped spans such as AAPL or H2O orthographically after semantic recognition. It does not spell ordinary lexical prose, does not claim a semantic domain, and never overrides caller-protected or auto-protected literal spans. SequenceFallbackMode, InterpretationMode, RecognitionDomain, and RecognitionEvidence are exported public types. Policy-suppressed candidates are visible in structured trace diagnostics.

The Swedish normalize_numbers(language="sv") path preserves comma-decimal precision and valid numeric grouping, while dates, digital times, and ambiguous dot forms remain caller-managed. Swedish structured quantities, temperatures, and SEK currency are owned by the locale grammar used by prepare().

The Vietnamese normalize_numbers(language="vi") path uses structured-safe normalization. Comma decimals preserve exact fractional precision, dot and space-family grouping are validated, and reviewed quantities and VND/₫ are locale-owned through abbr2words. Date, time, and ordinal semantics remain caller-managed.

Thai normalize_numbers(language="th") uses point decimals, comma or space-family grouping, accepts Thai digits, and preserves decimal precision digitwise. Structured quantities, temperatures, and THB/฿ use reviewed abbr2words identities while Numeralform owns numeric realization. th_TH and th-TH resolve to the Numeralform base locale; dates, times, eras, ordinals, ranges, and unsupported specialist semantics remain caller-managed or fail closed.

Russian normalize_numbers(language="ru") uses the structured-safe path. It preserves comma-decimal precision, accepts regular/NBSP/NNBSP grouping, and renders reviewed quantity grammar through abbr2words canonical IDs. Dates, digital times, year abbreviations, phone spans, RUB, and unreviewed specialist semantics remain caller-managed or fail closed. rus is accepted only as a compatibility alias.

Lexhint evidence provider

Lexhint 0.1.2 <= x < 0.3.0 is supported as an optional provider. Lexhint 0.1.x uses schema-7 artifacts, while Lexhint 0.2.x requires schema-8 artifacts installed separately with lexhint dataset download <language> --variant runtime. Spokenform never downloads datasets automatically.

Pass an installed Lexhint runtime Lexicon explicitly through lexical_evidence. No dataset is resolved or downloaded when the argument is omitted. URL lexical segmentation may work with a lexical-only provider. Contextual computing and sports support requires the semantic capability, and missing semantic evidence never vetoes an existing candidate.

from lexhint import Lexicon
from spokenform import prepare

lexicon = Lexicon("en", variant="runtime")
result = prepare(
    "compiler 8.3.2 and chatgpt.com",
    language="en",
    normalize_literals=True,
    lexical_evidence=lexicon,
    use_spacy=False,
)

Lexhint semantic domains are mapped to Spokenform use cases rather than copied into RecognitionDomain. Surface mode ignores semantic evidence. URL lexical evidence is a rendering aid for a URL already recognized or promoted by Spokenform.