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 source-aligned replacement metadata.

.. 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 the records from right to left reproduces the result exactly. expand_with_trace(...) remains as a compatibility view of the same result. Existing convenience calls continue to return strings.

from abbr2words import abbr2words_with_replacements

result = abbr2words_with_replacements("Prof. Klein, S. 12", lang="de")
print(result.text)
for replacement in result.replacements:
    print(replacement.start, replacement.end, replacement.text, replacement.kind)

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 two through eight 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" and are never claimed.

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’, aliases=()) :module: abbr2words

  Add or replace an abbreviation, optionally constrained by POS.

  A string is one POS label; collections support multiple labels. Deny
  constraints take precedence over allow constraints.

.. 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’) :module: abbr2words

  Register an entry using string-named context expansions.

.. 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.

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’) :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(entry) :module: abbr2words

  Add an abbreviation entry.

  :param entry: The abbreviation entry to add

.. 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’, aliases=()) :module: abbr2words

  Add or replace an entry using string context names and POS guards.

  A single POS string is treated as one label, while a collection can
  express several accepted or denied labels. Labels are normalized by
  :class:`AbbreviationEntry`.

.. 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 exact immutable replacement metadata.

.. 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) :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: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=’’) :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) :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.