API reference

Convenience functions

.. py:function:: abbr2words(text, *, lang=’en’, context=True, initialism_mode=’dotted_only’, initialism_case=’source’, registered_initialism_mode=’expand’, annotations=None, protected_spans=None) :module: abbr2words

Expand known abbreviations in text.

The function expands abbreviations only. It intentionally does not normalize dates, times, numbers, currencies, or general punctuation. Optional annotations must use character offsets in the original source; POS guards fail open when usable lexical evidence is missing, and numeric units remain authoritative over generic POS predictions.

.. py:function:: abbr2words_with_replacements(text, *, lang=’en’, context=True, initialism_mode=’dotted_only’, initialism_case=’source’, registered_initialism_mode=’expand’, annotations=None, protected_spans=None) :module: abbr2words

Expand text and return exact, immutable source-aligned replacement metadata.

Offsets refer to the original input. Records are deterministic and non-overlapping, include their matched source surface, and carry stable rule provenance; unit records may also expose canonical identity. Callers should consume result.replacements instead of diffing result.source_text and result.text.

.. py:function:: iter_unit_matches(text, language, *, overrides=None, suppressed=None, protected_spans=()) :module: abbr2words

Yield structured source-aligned matches for numeric quantity symbols.

.. py:function:: iter_unit_diagnostics(text, language, *, overrides=None, suppressed=None, protected_spans=()) :module: abbr2words

Yield accepted unit matches and policy rejections for compact candidates.

.. py:function:: iter_initialism_diagnostics(text, language=’en’, *, context=True, initialism_mode=’dotted_only’, initialism_case=’source’, registered_initialism_mode=’expand’, protected_spans=None) :module: abbr2words

Yield source-aligned decisions for initialism-shaped candidates.

abbr2words(..., annotations=...) accepts an iterable of source-aligned TokenAnnotation objects. Their offsets refer to the original input text; labels are normalized and overlapping or invalid spans raise ValueError. Missing lexical POS evidence fails open, and numeric unit guards remain authoritative.

The context enum includes DEFAULT, TITLE, PLACE, TIME, DATE, ACADEMIC, and RELIGIOUS. DATE is selected only by bounded numeric or date-punctuation evidence in the local source window; it does not parse dates. Language profiles may add stricter policies, and uncased scripts do not use the cased-letter title heuristic.

English context profiles use positive place evidence for ambiguous dotted spellings. Address/street evidence can expand a single compass letter, and explicit Washington/place evidence can expand D.C.; personal and bibliographic initials remain letter-spelled. Standalone uppercase dotted initialisms of two through eight letters use a low-priority source-grapheme fallback, so registered semantic rules such as e.g. and U.S. retain precedence while E.G. can become E G.

Abbreviation boundaries use symmetric Unicode word-character lookarounds: registered spellings may start or end with punctuation, but cannot attach to a surrounding \w character. Optional protected_spans=[(start, end), ...] prevents replacements in caller-owned ranges such as URLs, markup, or code.

For source-aligned diagnostics or downstream text alignment, use abbr2words_with_replacements(...) or Expander.expand_with_replacements(...). The immutable ExpansionResult contains the original source_text, expanded text, and deterministic, non-overlapping ExpansionReplacement records. Replacement offsets refer to the original input, and applying records from right to left reproduces the result exactly. Each record is self-contained: matched_text and its source_text alias contain the consumed source surface, rule_id identifies the producing rule, and unit records expose canonical_id when available. The records are the authoritative accepted-edit plan. Do not reconstruct these edits by diffing source_text and text. Existing convenience calls continue to return strings.

from abbr2words import abbr2words_with_replacements

source = "Prof. Klein, 500 g"
result = abbr2words_with_replacements(source, lang="de")
print(result.text)
for replacement in result.replacements:
    print(
        replacement.start,
        replacement.end,
        replacement.matched_text,
        replacement.text,
        replacement.kind,
        replacement.language,
        replacement.rule_id,
        replacement.canonical_id,
        replacement.context,
    )

For every replacement, 0 <= start <= end <= len(source_text), the source surface equals source_text[start:end], records are ordered and non-overlapping, and an unchanged input has text == source_text and no replacements. Protected spans are never claimed.

ExpansionKind = Literal["abbreviation", "unit"] bounds the replacement families. The fields have distinct meanings:

Field

Meaning

start, end

Original Python string character offsets.

matched_text

Exact source substring consumed by this edit.

source_text

Compatibility/readability alias of matched_text.

text

Replacement text generated by Abbr2words.

replacement

Compatibility alias of text.

kind

Bounded replacement family, abbreviation or unit.

language

Resolved language or locale used for the rule.

rule_id

Stable provenance/rule identifier.

rule

Existing stored compatibility field used by rule_id.

source

Legacy provenance field, not the matched source substring.

abbreviation

Canonical lexical registry identity when available.

canonical_id

Locale-independent unit identity when available.

priority

Resolver priority used during candidate selection.

context

Selected abbreviation context when applicable.

Do not parse source to recover matched source text. Use matched_text. Do not parse rule_id to recover unit identity. Use canonical_id. Replacement records returned by the expansion APIs always expose matched_text == source_text[start:end].

Initialism policies

The public expansion functions and get_expander(), get_shared_expander(), and Expander accept these optional policy arguments:

abbr2words(
    "NGO BBC PDF",
    initialism_mode="conservative_undotted",  # default: "dotted_only"
    initialism_case="lower",  # "source", "upper", or "lower"
    registered_initialism_mode="expand",  # or explicit "spell"
)

The default preserves existing behavior for unknown uppercase text. The reviewed registry intentionally owns a small set of common initialisms such as BBC, US, UK, ISBN, HTML, and TV, which render source graphemes as ordinary abbreviation entries. conservative_undotted recognizes only high-confidence standalone ASCII uppercase residuals from three through six letters and rejects reviewed lexical acronyms, ambiguous words, headline runs, Roman numerals, and structured identifiers. spell_undotted retains the broad historical opt-in behavior and renders standalone source-aligned graphemes. Neither mode parses numbers, URLs, e-mail addresses, versions, product codes, phone numbers, stock tickers, or Roman numerals. Callers should reserve typed structured spans first, then use the conservative policy for remaining uppercase tokens. registered_initialism_mode="spell" affects only reviewed registry entries carrying the explicit speech_strategy="spell_source" metadata; semantic registry expansions remain the default.

The compatibility surface is intentionally conservative:

Source

Detection mode

Case

Registered mode

Result

ABC

dotted_only

source

expand

A B C (reviewed entry)

NGO

conservative_undotted

source

expand

N G O (high-confidence residual)

ABC

spell_undotted

upper

expand

A B C

ABC

spell_undotted

lower

expand

a b c

U.S.

dotted

lower

spell

u s.

pp. 12

dotted

source

spell

p p 12

The final period in the U.S. row is source sentence punctuation retained by the existing replacement policy. Reviewed entries continue to outrank the generic fallback, while Roman-only tokens and structured identifier components remain excluded.

The shared-expander cache includes all policy values, so expanders with different initialism behavior are independent instances. Fallback replacement records use abbr:initialism for dotted matches and abbr:initialism-conservative or abbr:initialism-undotted for undotted matches. iter_initialism_diagnostics() reports source-aligned start/end, source_text, language, candidate_kind, decision, stable reason, and registered_entry_id fields. Protected spans are reported as reason="protected-span" and are never claimed.

For repository maintenance, scripts/report_initialism_candidates.py groups fresh benchmark failures by unresolved candidate token using the same diagnostic surface. It accepts JSONL or JSON rows with source text, language, expected output, and optional actual output, then reports grouped token counts, locales, reasons, Roman/vowel/two-letter flags, registry coverage, protection flags, uppercase-run evidence, and sample source sentences. The helper is for reviewing candidate additions after benchmark reruns; it does not change the runtime matching policy.

The bundled language registry follows a 66-key current-master parity snapshot: 49 base keys plus the explicit locale overlays en_GB, en_IN, en_NG, en_US, es_CO, es_CR, es_GT, es_MX, es_NI, es_VE, fr_BE, fr_CH, fr_DZ, pt_BR, zh_CN, zh_HK, and zh_TW. normalize_language() returns an exact locale key when registered and otherwise its base key. Turkish unit symbols followed by straight or curly apostrophe suffixes are intentionally not expanded until suffix realization is implemented.

Bundled identity lexical rules are rejected. Locale currencies and similar structured identities are recognized only in numeric quantity context, while iter_unit_matches() remains the semantic API for canonical IDs and exact source offsets. Non-English baseline unit replacement text is a localized neutral label, not a complete quantity grammar.

.. py:function:: expand(text, *, lang=’en’, context=True, initialism_mode=’dotted_only’, initialism_case=’source’, registered_initialism_mode=’expand’, annotations=None, protected_spans=None) :module: abbr2words

Expand known abbreviations in text.

The function expands abbreviations only. It intentionally does not normalize dates, times, numbers, currencies, or general punctuation. Optional annotations must use character offsets in the original source; POS guards fail open when usable lexical evidence is missing, and numeric units remain authoritative over generic POS predictions.

.. py:function:: normalize_language(lang) :module: abbr2words

Normalize and resolve an ISO-style language or locale code.

.. py:function:: base_language(lang) :module: abbr2words

Return the resolved base language for a language or locale input.

.. py:function:: supported_languages(*, include_locales=True) :module: abbr2words

Return sorted bundled language and, optionally, locale keys.

.. py:function:: get_expander(lang=’en’, *, context=True, initialism_mode=’dotted_only’, initialism_case=’source’, registered_initialism_mode=’expand’) :module: abbr2words

Return a new, independently mutable language expander.

.. py:function:: get_shared_expander(lang=’en’, *, context=True, initialism_mode=’dotted_only’, initialism_case=’source’, registered_initialism_mode=’expand’) :module: abbr2words

Return the shared registry for a language, context, and policy.

.. py:function:: reset_expanders(lang=None) :module: abbr2words

Reset one or all shared language registries.

Mutable facade

.. py:class:: Expander(lang=’en’, *, context=True, initialism_mode=’dotted_only’, initialism_case=’source’, registered_initialism_mode=’expand’) :module: abbr2words :canonical: abbr2words.api.Expander

Small facade for a mutable, language-specific abbreviation registry.

.. py:method:: Expander.call(text, *, annotations=None, protected_spans=None) :module: abbr2words

  Expand abbreviations using this instance's registry.

  ``annotations`` are source-aligned to the original text. Only coarse
  ``pos`` labels participate in guards; fine-grained ``tag`` values are
  retained as metadata.

.. py:method:: Expander.abbreviations() :module: abbr2words

  Return the configured abbreviation spellings.

.. py:method:: Expander.add(abbreviation, expansion, *, context_expansions=None, case_sensitive=False, description=’’, only_if_preceded_by=None, only_if_followed_by=None, only_if_pos=None, not_if_pos=None, case_policy=’fixed’, speech_strategy=’expand’, spoken_form=None, aliases=()) :module: abbr2words

  Add or replace a custom abbreviation.

.. py:method:: Expander.add_custom_abbreviation(abbreviation, expansion, description=’’, case_sensitive=False, only_if_preceded_by=None, only_if_followed_by=None, only_if_pos=None, not_if_pos=None, case_policy=’fixed’, speech_strategy=’expand’, spoken_form=None, aliases=()) :module: abbr2words

  Register an entry using string-named context expansions.

.. py:method:: Expander.add_many(entries, *, on_conflict=’error’) :module: abbr2words

  Atomically register a batch of abbreviation entries.

.. py:method:: Expander.expand(text, *, annotations=None, protected_spans=None) :module: abbr2words

  Expand abbreviations using this instance's registry.

  ``annotations`` are source-aligned to the original text. Only coarse
  ``pos`` labels participate in guards; fine-grained ``tag`` values are
  retained as metadata.

.. py:method:: Expander.expand_with_replacements(text, *, annotations=None, protected_spans=None) :module: abbr2words

  Expand abbreviations and return exact replacement metadata.

.. py:method:: Expander.expand_with_trace(text, *, annotations=None, protected_spans=None) :module: abbr2words

  Compatibility alias for :meth:`expand_with_replacements`.

.. py:method:: Expander.has(abbreviation, *, case_sensitive=False) :module: abbr2words

  Return whether this instance contains an abbreviation.

.. py:method:: Expander.iter_initialism_diagnostics(text, *, protected_spans=None) :module: abbr2words

  Yield source-aligned initialism decisions for this expander.

.. py:method:: Expander.iter_unit_matches(text, *, protected_spans=()) :module: abbr2words

  Yield structured matches using this expander's unit customization.

.. py:method:: Expander.remove(abbreviation, *, case_sensitive=False) :module: abbr2words

  Remove an abbreviation from this instance.

.. py:method:: Expander.remove_unit(symbol) :module: abbr2words

  Suppress a reviewed unit for this isolated expander.

.. py:method:: Expander.set_unit(symbol, expansion, *, case_sensitive=True, description=’Custom unit’, canonical_id=None, category=’unit’) :module: abbr2words

  Override a reviewed unit for this isolated expander.

Guarded unit symbols

The stable API expands a reviewed set of unit symbols only when a numeric value precedes the complete unit expression. Numeric forms such as 500 g, 500g, 1.5 kg, 1,5 kg, and 5 km/h are supported; standalone symbols and attached words remain unchanged. This is symbol expansion, not number spelling, unit conversion, or universal UCUM parsing.

The matcher is maximal and fail-closed: larger unsupported expressions such as 5 km / h, 1 m^2, and 2kg-rated remain unchanged instead of being partially rewritten. Reviewed aliases include both µg and μg; unrelated source characters are not Unicode-normalized. Unit metadata controls case sensitivity, whether a numeric value is required, and whether a separator is required between a numeric value and an ambiguous one-letter symbol. The separator requirement defaults to false for compatibility; reviewed B, A, and K candidates require spacing so compact identifier-like forms are not claimed as units.

Unit replacements have kind="unit" in the exact replacement result. This layer expands unit symbols/abbreviations lexically; it does not verbalize the numeric quantity or choose grammatical singular/plural forms. Callers that need a phrase such as zwei Minuten should consume the complete numeric quantity in a structured quantity stage before calling abbreviation expansion.

abbr2words("500 g", lang="en")  # "500 gram"
abbr2words("section g", lang="en")  # "section g"

Structured quantity matches

Use iter_unit_matches() when a downstream semantic stage needs the recognized quantity before it performs number or grammar realization:

from abbr2words import iter_unit_matches

source = "Für 1,5 kg Mehl"
match = next(iter_unit_matches(source, "de"))
assert source[match.start : match.end] == "1,5 kg"
assert source[match.value_start : match.value_end] == "1,5"
assert match.value == "1,5"
assert match.symbol == "kg"
assert match.canonical_id == "mass-kilogram"

UnitMatch is immutable and source-aligned. Its start:end range covers the complete numeric expression and symbol; value_start:value_end identifies the original numeric lexeme exactly. Matches are deterministic, maximal, and non-overlapping. protected_spans=[(start, end), ...] suppresses caller-owned ranges such as markup, URLs, or code. overrides and suppressed accept unit symbols; suppression also accepts a canonical ID.

iter_unit_diagnostics() returns the same accepted decisions plus compact separator-policy rejections with status="rejected" and reason="requires_separator". Each record retains the symbol, locale, and canonical identity so downstream ownership diagnostics do not need to infer decisions from replacement text.

The matcher recognizes and identifies quantity symbols. It does not decide how the complete quantity is spoken: number-to-words conversion, singular/plural grammar, currency decomposition, and locale-specific decimal policy belong to the consuming semantic normalizer. Currency and magnitude matches expose their category without turning this package into a structured-number parser.

Reviewed semantic identities include speed, pressure, data, fuel-consumption, and flow units plus JPY, CHF, INR, KRW, and MXN currencies. The es_MX overlay gives unqualified $ the Mexican-peso identity while US$ and USD remain US dollar. These are recognition contracts for a downstream consumer, not amount or number grammar.

Custom glossary API

CasePolicy and SpeechStrategy are public typing aliases. CasePolicy is "fixed" or "sentence"; SpeechStrategy is "expand", "spell_source", or "custom". AbbreviationEntry.spoken_form stores an explicit non-empty lexical realization for the custom strategy while expansion remains the semantic form.

The mutable expanders expose equivalent custom-entry builders:

expander.add(
    "AAA",
    "anti-aircraft artillery",
    speech_strategy="custom",
    spoken_form="Triple A",
    aliases=("A.A.A.",),
)
expander.add_custom_abbreviation(
    "Ref.",
    {"default": "reference", "title": "referee"},
    speech_strategy="expand",
)

spell_source uses the matched source spelling only when registered_initialism_mode="spell" is selected. custom is independent of that mode and does not apply sentence casing.

Expander.add_many(entries, on_conflict=...) accepts an iterable of AbbreviationEntry values and validates the full batch before committing it. The on_conflict values are "error" and "replace". The result is a BulkAddResult with added and replaced counts and canonical names. The error case raises AbbreviationConflictError, whose immutable conflicts records identify the colliding key, incoming and existing abbreviations, and whether the conflict is a duplicate, canonical collision, or alias collision. Aliases participate in matching and conflict checks.

The bulk operation and all custom registration methods are instance-local. Build and customize an isolated expander before sharing it for read-only expansion; concurrent mutation of one expander is not a supported synchronization model. This API remains lexical and does not provide profile serialization, web import, or date, time, number, URL, or general speech normalization.

Core types

.. py:class:: TokenAnnotation(start, end, pos=None, tag=None) :module: abbr2words :canonical: abbr2words.annotations.TokenAnnotation

A provider-neutral token annotation aligned to the source text.

Offsets use Python string indices: text[start:end]. pos is normally an uppercase coarse Universal POS label; tag may contain a provider-specific fine-grained tag.

.. py:class:: AbbreviationEntry(abbreviation, expansion, context_expansions=None, variants=(), case_sensitive=False, description=’’, only_if_preceded_by=None, only_if_followed_by=None, only_if_pos=None, not_if_pos=None, boundary=’word’, left_boundary=None, right_boundary=None, origin=’bundled’, aliases=(), case_policy=’fixed’, speech_strategy=’expand’, preserve_sentence_final_period=True, spoken_form=None) :module: abbr2words :canonical: abbr2words.core.AbbreviationEntry

A single abbreviation with its expansion(s).

.. attribute:: abbreviation

  The abbreviated form (e.g., "Prof.")

  :type: str

.. attribute:: expansion

  Default expansion (e.g., "Professor")

  :type: str

.. attribute:: context_expansions

  Optional dict of context-specific expansions

  :type: dict[abbr2words.core.AbbreviationContext, str] | None

.. attribute:: case_sensitive

  Whether matching should be case-sensitive

  :type: bool

.. attribute:: description

  Human-readable description of the abbreviation

  :type: str

.. attribute:: only_if_preceded_by

  Optional regex that must match the text immediately
  before the abbreviation match (typically anchored with $).

  :type: str | re.Pattern[str] | None

.. attribute:: only_if_followed_by

  Optional regex that must match the suffix immediately
  after the abbreviation match. The pattern is matched against
  ``text[end:]``; therefore ``^`` means immediately after this
  candidate, not the beginning of the complete source string.

  :type: str | re.Pattern[str] | None

.. attribute:: only_if_pos

  Optional coarse POS label or labels. POS evidence is
  evaluated only when usable source-aligned annotations are present.

  :type: str | collections.abc.Collection[str] | None

.. attribute:: not_if_pos

  Optional coarse POS label or labels that veto a match when
  they overlap the abbreviation. This guard takes precedence over
  ``only_if_pos``.

  :type: str | collections.abc.Collection[str] | None

.. py:method:: AbbreviationEntry.get_expansion(context=None) :module: abbr2words

  Get the appropriate expansion for the given context.

  :param context: The context type, or None for default

  :returns: The expanded form

AbbreviationEntry.variants is an ordered tuple of immutable, declarative ExpansionVariant values. The first variant whose guards match the original source wins, followed by existing context and default expansion fallback. Variants do not accept callbacks.

AbbreviationEntry.case_policy is "fixed" by default. Set it to "sentence" only for reviewed lexical expansions whose canonical stored form is appropriate in mid-sentence text. The matcher applies it after selecting a variant or context expansion, and aliases share the entry policy. Dotted abbreviations retain one final period when their consumed dot is also sentence-final.

.. py:class:: ExpansionVariant(expansion, only_if_preceded_by=None, only_if_followed_by=None, only_if_pos=None, not_if_pos=None) :module: abbr2words :canonical: abbr2words.core.ExpansionVariant

One ordered, declarative conditional expansion for an abbreviation.

Variants deliberately reuse the entry guard vocabulary instead of accepting callbacks. This keeps registry data serializable and makes selection deterministic and safe to evaluate against the original source text.

AbbreviationEntry.only_if_pos and not_if_pos accept coarse POS labels such as NOUN, PROPN, and ADP. They are evaluated only when annotations are provided. Expander.add() exposes the same optional only_if_pos and not_if_pos keyword arguments, plus aliases=(...) for additional source spellings that share the entry’s guards, case policy, and speech strategy.

The abbreviation stage returns lexical replacements with source-aligned spans; it does not interpret following numbers, dates, decimals, structured identifiers, article elision, surrounding grammar, or speech rendering. Those concerns remain with the consuming normalizer, including spokenform.

.. py:class:: AbbreviationContext(*values) :module: abbr2words :canonical: abbr2words.core.AbbreviationContext

Context types for disambiguating abbreviations.

.. py:class:: ExpansionMatch(start, end, source_text, replacement, language, entry_id, kind, context, priority) :module: abbr2words :canonical: abbr2words.core.ExpansionMatch

One accepted source-aligned expansion from :meth:expand_with_trace.

.. py:class:: AbbreviationExpander(enable_context_detection=True, *, initialism_mode=’dotted_only’, initialism_case=’source’, registered_initialism_mode=’expand’) :module: abbr2words :canonical: abbr2words.core.AbbreviationExpander

Abstract base class for language-specific abbreviation expanders.

.. py:method:: AbbreviationExpander.add(abbreviation, expansion, *, context_expansions=None, case_sensitive=False, description=’’, only_if_preceded_by=None, only_if_followed_by=None, only_if_pos=None, not_if_pos=None, case_policy=’fixed’, speech_strategy=’expand’, spoken_form=None, aliases=()) :module: abbr2words

  Build and register a custom abbreviation entry.

.. py:method:: AbbreviationExpander.add_abbreviation(entry) :module: abbr2words

  Add or replace an abbreviation entry.

.. py:method:: AbbreviationExpander.add_custom_abbreviation(abbreviation, expansion, description=’’, case_sensitive=False, only_if_preceded_by=None, only_if_followed_by=None, only_if_pos=None, not_if_pos=None, case_policy=’fixed’, speech_strategy=’expand’, spoken_form=None, aliases=()) :module: abbr2words

  Add or replace an entry using string-named context expansions.

.. py:method:: AbbreviationExpander.add_many(entries, *, on_conflict=’error’) :module: abbr2words

  Atomically register a batch of abbreviation entries.

.. py:method:: AbbreviationExpander.expand(text, *, annotations=None, protected_spans=None) :module: abbr2words

  Expand all abbreviations in the text.

  :param text: Input text containing abbreviations
  :param annotations: Optional provider-neutral annotations aligned to the
                      original source offsets. Incomplete lexical POS evidence does
                      not suppress a structurally valid match.

  :returns: Text with abbreviations expanded

.. py:method:: AbbreviationExpander.expand_with_replacements(text, *, annotations=None, protected_spans=None) :module: abbr2words

  Expand text and return the immutable source-aligned accepted-edit plan.

  Offsets refer to the original input. Returned records are ordered,
  non-overlapping, expose exact matched source text, and reconstruct
  ``result.text`` when applied right-to-left.

.. py:method:: AbbreviationExpander.expand_with_trace(text, *, annotations=None, protected_spans=None) :module: abbr2words

  Compatibility alias for :meth:`expand_with_replacements`.

.. py:method:: AbbreviationExpander.get_abbreviation(abbreviation, case_sensitive=False) :module: abbr2words

  Get an abbreviation entry.

  :param abbreviation: The abbreviation to retrieve (e.g., "Dr.")
  :param case_sensitive: Whether to match case-sensitively

  :returns: The abbreviation entry if found, None otherwise

.. py:method:: AbbreviationExpander.get_abbreviations_list() :module: abbr2words

  Get a list of all supported abbreviations.

  :returns: List of abbreviation strings

.. py:method:: AbbreviationExpander.has_abbreviation(abbreviation, case_sensitive=False) :module: abbr2words

  Check if an abbreviation exists.

  :param abbreviation: The abbreviation to check (e.g., "Dr.")
  :param case_sensitive: Whether to match case-sensitively

  :returns: True if the abbreviation exists, False otherwise

.. py:method:: AbbreviationExpander.iter_initialism_diagnostics(text, *, protected_spans=()) :module: abbr2words

  Yield initialism decisions using this expander's registry and policy.

.. py:method:: AbbreviationExpander.iter_unit_matches(text, *, protected_spans=()) :module: abbr2words

  Yield structured matches using this expander's unit customization.

.. py:method:: AbbreviationExpander.remove_abbreviation(abbreviation, case_sensitive=False) :module: abbr2words

  Remove an abbreviation entry.

  :param abbreviation: The abbreviation to remove (e.g., "Dr.")
  :param case_sensitive: Whether to match case-sensitively

  :returns: True if the abbreviation was found and removed, False otherwise

.. py:method:: AbbreviationExpander.remove_unit(symbol) :module: abbr2words

  Suppress a bundled unit or remove an instance-local unit override.

.. py:method:: AbbreviationExpander.set_unit(symbol, expansion, *, case_sensitive=True, description=’Custom unit’, canonical_id=None, category=’unit’) :module: abbr2words

  Override one reviewed unit for this expander instance.

.. py:class:: ExpansionReplacement(start, end, text, source, kind, language, abbreviation=None, rule=None, priority=0, context=None, matched_text=’’, canonical_id=None) :module: abbr2words :canonical: abbr2words.core.ExpansionReplacement

One accepted replacement against the original source text.

.. py:property:: ExpansionReplacement.entry_id :module: abbr2words :type: str

  Compatibility alias for the stable rule/source identifier.

.. py:property:: ExpansionReplacement.replacement :module: abbr2words :type: str

  Compatibility alias for the replacement text.

.. py:property:: ExpansionReplacement.rule_id :module: abbr2words :type: str

  Stable identifier for the rule that produced this replacement.

.. py:property:: ExpansionReplacement.source_text :module: abbr2words :type: str

  Original source substring consumed by this replacement.

.. py:class:: ExpansionResult(source_text, text, replacements) :module: abbr2words :canonical: abbr2words.core.ExpansionResult

Expanded text together with accepted source replacements.

.. py:property:: ExpansionResult.matches :module: abbr2words :type: tuple[~abbr2words.core.ExpansionMatch, …]

  Return the legacy trace view of :attr:`replacements`.

.. py:class:: UnitMatch(start, end, value_start, value_end, value, symbol, canonical_id, canonical_symbol, expansion, language, category=’unit’, ambiguity=’none’, separator=’’, quantity_template=None) :module: abbr2words :canonical: abbr2words.units.UnitMatch

One immutable, source-aligned recognized numeric quantity symbol.

.. py:class:: ProtectedSpan(start, end, kind=None) :module: abbr2words :canonical: abbr2words.core.ProtectedSpan

A source range that must not be changed by expansion.

.. py:class:: UnitEntry(symbols, expansion, case_sensitive=True, description=’’, canonical_symbol=None, requires_numeric_value=True, canonical_id=None, reject_following_apostrophe=False, category=’unit’, quantity_position=’suffix’, allow_lexical_overlap=False, preserve_sentence_final_period=False, reject_following_period=False, requires_separator=False, quantity_template=None) :module: abbr2words :canonical: abbr2words.units.UnitEntry

A localized unit spelling recognized only after a numeric quantity.

The public abbr2words.core.abbreviation_guards_match() helper accepts either an AnnotationIndex or an annotation iterable. Iterable input is normalized and validated the same way as expansion. It evaluates coarse pos only; provider-specific tag values are retained but not matched.