Skip to content

Linguistic Template Features

This guide covers VIBE's powerful linguistic features, which go beyond simple variable replacement. These tools help you correctly handle plurals, possessives, complex list formatting, and number-to-word conversions. It assumes you are familiar with the basics from the Jinja Essentials guide.

Supported Languages: English (en), Swedish (sv), German (de), French (fr), Spanish (es)

Locale-Specific Aliases: Each language provides native-language aliases for filters and tags, making templates more readable when written in that language. See the Locale-Specific Aliases section at the end of this guide.

Core Concept: The Linguistic Object

Behind the scenes, these filters work by passing a special Linguistic object between them. When you apply the first filter in a chain (like | plural), VIBE converts your string into this object. Subsequent filters then modify the object's properties (like its number or definiteness) before it's finally converted back to a string for printing.

This allows for powerful, context-aware transformations, such as | plural | definite correctly applying the plural definite form.

Basic Linguistic Filters

These filters can be applied to any string variable or literal.

| plural

Converts a word to its plural form based on the current language.

Arguments:

  • "overrides" (optional, first positional): Override irregular forms using "singular->plural" syntax. Multiple overrides can be comma-separated.
  • if_=variable (optional): Prevents pluralization if variable has a value of 1.

Screenshot

{# English #}
You have {{ num_items }} {{ "renewal period" | plural(if_=num_items) }}.
{# num_items=1: "You have 1 renewal period." #}
{# num_items=2: "You have 2 renewal periods." #}

{# Swedish (alias: plural) #}
Det finns {{ antal }} {{ "avtal" | plural(if_=antal) }}.
{# antal=1: "Det finns 1 avtal." #}
{# antal=2: "Det finns 2 avtal." #}

{# German (alias: plural) #}
Es gibt {{ anzahl }} {{ "Vertrag" | plural(if_=anzahl) }}.
{# anzahl=1: "Es gibt 1 Vertrag." #}
{# anzahl=2: "Es gibt 2 Verträge." #}

{# French (alias: pluriel) #}
Il y a {{ nombre }} {{ "contrat" | pluriel(if_=nombre) }}.
{# nombre=1: "Il y a 1 contrat." #}
{# nombre=2: "Il y a 2 contrats." #}

{# Spanish (alias: plural) #}
Hay {{ cantidad }} {{ "contrato" | plural(if_=cantidad) }}.
{# cantidad=1: "Hay 1 contrato." #}
{# cantidad=2: "Hay 2 contratos." #}

Overriding Irregular Plurals

When a word has an irregular plural form not in the built-in lexicon, you can provide an override:

{# Swedish - override for uncommon words #}
{{ "gås" | plural("gås->gäss") }}
{# Output: "gäss" #}

{# Multiple overrides #}
{{ djur | plural("gås->gäss,åsna->åsnor") }}

{# English - override for domain-specific terms #}
{{ "vertex" | plural("vertex->vertices") }}
{# Output: "vertices" (also in built-in lexicon) #}

{# German #}
{{ "Virus" | plural("Virus->Viren") }}
{# Output: "Viren" #}

{# French #}
{{ "festival" | plural("festival->festivals") }}
{# Output: "festivals" (exception to -al -> -aux rule) #}

{# Spanish #}
{{ "curriculum" | plural("curriculum->curricula") }}
{# Output: "curricula" #}

The override takes precedence over both built-in irregular forms and heuristic rules.

| definite

Converts a word or phrase to its definite form. In English, this adds "the". In Swedish, it adds a suffix. In German/French/Spanish, it adds the appropriate article.

Arguments:

  • "overrides" (optional, first positional): Override irregular forms using "word->definite_form" syntax. Primarily useful for Swedish where definiteness is a suffix.
{# English #}
The subject of the agreement is {{ item | definite }}.
{# item="opportunity" -> "the opportunity" #}

{# Swedish (alias: bestamd) #}
Avtalet avser {{ item | bestamd }}.
{# item="verksamhet" -> "verksamheten" #}
{# item="avtal" -> "avtalet" #}

{# Swedish - override for uncommon words #}
{{ "test" | bestamd("test->testet") }}
{# Output: "testet" #}

{# German (alias: bestimmt) #}
Der Gegenstand ist {{ item | bestimmt }}.
{# item="Vertrag" -> "der Vertrag" #}
{# item="Vereinbarung" -> "die Vereinbarung" #}

{# French (alias: defini) #}
L'objet est {{ item | defini }}.
{# item="contrat" -> "le contrat" #}
{# item="partie" -> "la partie" #}
{# item="accord" -> "l'accord" #}

{# Spanish (alias: definido) #}
El objeto es {{ item | definido }}.
{# item="contrato" -> "el contrato" #}
{# item="parte" -> "la parte" #}

Note: Overrides are most useful for Swedish, where definiteness is expressed as a suffix with complex rules. For German, French, and Spanish, the definite article is determined by grammatical gender, which is looked up in the noun lexicon or guessed from word endings.

| possessive

Converts a word or phrase to its possessive/genitive form.

  • possessive(subject="..."): A special argument that can rephrase the entire expression.
{# English #}
This marks {{ "the contract" | possessive(subject="end") }}.
{# Output: "the end of the contract" #}
{{ "Company" | possessive }}
{# Output: "Company's" #}

{# Swedish (alias: genitiv) #}
{{ "Bolaget" | genitiv(subject="åtaganden") }}
{# Output: "Bolagets åtaganden" #}
{{ "Förlängningsperiod" | bestamd | genitiv }}
{# Output: "Förlängningsperiodens" #}

{# German (alias: genitiv) #}
{{ "Vertrag" | genitiv }}
{# Output: "des Vertrags" #}
{{ "Vereinbarung" | genitiv }}
{# Output: "der Vereinbarung" #}

{# French (alias: possessif) #}
{{ "contrat" | possessif }}
{# Output: "du contrat" #}
{{ "accord" | possessif }}
{# Output: "de l'accord" #}

{# Spanish (alias: posesivo) #}
{{ "contrato" | posesivo }}
{# Output: "del contrato" #}
{{ "parte" | posesivo }}
{# Output: "de la parte" #}

| indefinite

Adds an indefinite article to a word. In English, this correctly handles "a" vs "an" based on pronunciation.

{# English #}
This is {{ "agreement" | indefinite }}.
{# Output: "an agreement" #}
This is {{ "contract" | indefinite }}.
{# Output: "a contract" #}
This is {{ "university" | indefinite }}.
{# Output: "a university" (sounds like "yoo") #}
This is {{ "hour" | indefinite }}.
{# Output: "an hour" (silent h) #}

{# Swedish (alias: obestamd) #}
Detta är {{ "avtal" | obestamd }}.
{# Output: "ett avtal" (neuter) #}
Detta är {{ "kund" | obestamd }}.
{# Output: "en kund" (common) #}

{# German (alias: unbestimmt) #}
Das ist {{ "Vertrag" | unbestimmt }}.
{# Output: "ein Vertrag" (masculine) #}
Das ist {{ "Vereinbarung" | unbestimmt }}.
{# Output: "eine Vereinbarung" (feminine) #}
Das ist {{ "Dokument" | unbestimmt }}.
{# Output: "ein Dokument" (neuter) #}

{# French (alias: indefini) #}
C'est {{ "contrat" | indefini }}.
{# Output: "un contrat" (masculine) #}
C'est {{ "partie" | indefini }}.
{# Output: "une partie" (feminine) #}

{# Spanish (alias: indefinido) #}
Es {{ "contrato" | indefinido }}.
{# Output: "un contrato" (masculine) #}
Es {{ "parte" | indefinido }}.
{# Output: "una parte" (feminine) #}

| itemize

Handles phrases with multiple items, like "goods and services". It controls the conjunction word used when the phrase is finally printed.

{# English #}
{{ "goods, support and services" | definite | itemize(conjunction="or") }}
{# Output: "the goods, the support, or the services" #}

{# Swedish (alias: upprakning) #}
{{ "varor, stöd och tjänster" | bestamd | upprakning(konjunktion="eller") }}
{# Output: "varorna, stödet eller tjänsterna" #}

{# German (alias: auflistung) #}
{{ "Waren, Support und Dienstleistungen" | bestimmt | auflistung(konjunktion="oder") }}
{# Output: "die Waren, der Support oder die Dienstleistungen" #}

{# French (alias: enumerer) #}
{{ "biens, support et services" | defini | enumerer(conjonction="ou") }}
{# Output: "les biens, le support ou les services" #}

{# Spanish (alias: enumerar) #}
{{ "bienes, soporte y servicios" | definido | enumerar(conjuncion="o") }}
{# Output: "los bienes, el soporte o los servicios" #}

Company Name Utilities

| strip_company_form

Strips legal company form designations from a company name, converting a formal name like "Inka Interactive Ekonomisk förening" to the colloquial "Inka Interactive".

Currently supported: Swedish (sv) only. For all other locales, the filter is a no-op and returns the input unchanged.

Swedish alias: utan_bolagsform

The filter handles both suffix and prefix positions with word-boundary awareness, so names like "ABB" or "IKEA" are not mangled.

{# Swedish — suffix forms (most common) #}
{{ sokande_namn | strip_company_form }}
{# "Inka Interactive Ekonomisk förening" → "Inka Interactive" #}
{# "Volvo AB" → "Volvo" #}
{# "Sjöutsikten Brf" → "Sjöutsikten" #}

{# Swedish — prefix forms #}
{{ "AB Volvo" | strip_company_form }}
{# Output: "Volvo" #}

{# Swedish alias #}
{{ sokande_namn | utan_bolagsform }}
{# "Bygg & Montage HB" → "Bygg & Montage" #}

{# English — no-op #}
{{ "Volvo AB" | strip_company_form }}
{# Output: "Volvo AB" (unchanged) #}

Recognized Swedish company forms:

Full form Abbreviation(s)
Aktiebolag AB
Kommanditbolag KB
Handelsbolag HB
Ekonomisk förening Ek. för., Ek.för.
Bostadsrättsförening Brf, Brf.
Ideell förening Id. för., Id.för.
Enskild firma EF
Stiftelse
Samfällighetsförening
Kooperativ hyresrättsförening

Note: The filter is case-insensitive ("volvo ab""volvo", "Volvo AB""Volvo"). If no company form is found, the input is returned unchanged.

Number-to-Word Conversion Filters

For legal and formal documents, it's often necessary to write out numbers as words. These filters handle the conversion automatically based on the current locale.

| cardinal

Converts a number into its cardinal word form (e.g., one, two, three).

  • cardinal(cutoff=number): An optional argument. If the input number is greater than the cutoff, the filter will return the number as digits instead of words.
{# English #}
The term is {{ years | cardinal }} years.
{# years=3: "The term is three years." #}
{{ amount | cardinal(cutoff=100) }} dollars
{# amount=95: "ninety-five dollars" #}
{# amount=101: "101 dollars" #}

{# Swedish (alias: kardinal) #}
Avtalet gäller i {{ ar | kardinal }} år.
{# ar=3: "Avtalet gäller i tre år." #}
{# ar=21: "Avtalet gäller i tjugoett år." #}

{# German (alias: kardinal) #}
Der Vertrag gilt für {{ jahre | kardinal }} Jahre.
{# jahre=3: "Der Vertrag gilt für drei Jahre." #}
{# jahre=21: "Der Vertrag gilt für einundzwanzig Jahre." #}

{# French (alias: cardinal) #}
Le contrat dure {{ annees | cardinal }} ans.
{# annees=3: "Le contrat dure trois ans." #}
{# annees=80: "Le contrat dure quatre-vingts ans." #}

{# Spanish (alias: cardinal) #}
El contrato dura {{ anos | cardinal }} años.
{# anos=3: "El contrato dura tres años." #}
{# anos=21: "El contrato dura veintiuno años." #}

| ordinal

Converts a number into its ordinal word form (e.g., first, second, third).

{# English #}
This is the {{ count | ordinal }} amendment.
{# count=1: "the first amendment" #}
{# count=22: "the twenty-second amendment" #}

{# Swedish (alias: ordinal) #}
Detta är det {{ nummer | ordinal }} tillägget.
{# nummer=1: "det första tillägget" #}
{# nummer=3: "det tredje tillägget" #}

{# German (alias: ordinal) #}
Dies ist die {{ nummer | ordinal }} Änderung.
{# nummer=1: "die erste Änderung" #}
{# nummer=3: "die dritte Änderung" #}

{# French (alias: ordinal) #}
C'est le {{ numero | ordinal }} amendement.
{# numero=1: "le premier amendement" #}
{# numero=2: "le deuxième amendement" #}

{# Spanish (alias: ordinal) #}
Esta es la {{ numero | ordinal }} enmienda.
{# numero=1: "la primera enmienda" #}
{# numero=2: "la segunda enmienda" #}

| doublet

Creates a "doublet" format common in legal documents, showing both the word and digit form.

  • doublet(unit="..."): An optional argument to append a unit to the end.
{# English #}
{{ days | doublet }} days
{# days=30: "thirty (30) days" #}
{{ days | doublet(unit="business days") }}
{# days=30: "thirty (30) business days" #}

{# Swedish (alias: dublett) #}
{{ dagar | dublett }} dagar
{# dagar=30: "trettio (30) dagar" #}
{{ dagar | dublett(unit="arbetsdagar") }}
{# dagar=30: "trettio (30) arbetsdagar" #}

{# German (alias: wort_und_zahl) #}
{{ tage | wort_und_zahl }} Tage
{# tage=30: "dreißig (30) Tage" #}
{{ tage | wort_und_zahl(unit="Werktage") }}
{# tage=30: "dreißig (30) Werktage" #}

{# French (alias: doublet) #}
{{ jours | doublet }} jours
{# jours=30: "trente (30) jours" #}
{{ jours | doublet(unit="jours ouvrables") }}
{# jours=30: "trente (30) jours ouvrables" #}

{# Spanish (alias: doblete) #}
{{ dias | doblete }} días
{# dias=30: "treinta (30) días" #}
{{ dias | doblete(unit="días hábiles") }}
{# dias=30: "treinta (30) días hábiles" #}

| roman

Converts a number to Roman numerals. Useful for legal section numbering.

  • roman(case="upper") (default): Uppercase numerals (I, II, III)
  • roman(case="lower"): Lowercase numerals (i, ii, iii)
{# All languages - Roman numerals are universal #}

{# English #}
Article {{ section | roman }}
{# section=4: "Article IV" #}
{# section=1999: "Article MCMXCIX" #}

{# Swedish (alias: romersk) #}
Avsnitt {{ avsnitt | romersk }}
{# avsnitt=4: "Avsnitt IV" #}
{{ avsnitt | romersk(case="lower") }}
{# avsnitt=4: "iv" #}

{# German (alias: romisch) #}
Abschnitt {{ abschnitt | romisch }}
{# abschnitt=4: "Abschnitt IV" #}

{# French (alias: romain) #}
Section {{ section | romain }}
{# section=4: "Section IV" #}

{# Spanish (alias: romano) #}
Sección {{ seccion | romano }}
{# seccion=4: "Sección IV" #}

Note: Values outside 1-3999 are returned unchanged.

| alpha

Converts a number to an alphabetical label (1=A, 2=B, ..., 26=Z). Useful for list item markers.

  • alpha(case="upper") (default): Uppercase letters (A, B, C)
  • alpha(case="lower"): Lowercase letters (a, b, c)
{# All languages - alphabetical labels are universal #}

{# English #}
Exhibit {{ num | alpha }}
{# num=1: "Exhibit A" #}
{# num=26: "Exhibit Z" #}

{# Swedish (alias: alfabetisk) #}
Bilaga {{ num | alfabetisk }}
{# num=1: "Bilaga A" #}
{{ num | alfabetisk(case="lower") }}
{# num=1: "a" #}

{# German (alias: alphabetisch) #}
Anlage {{ num | alphabetisch }}
{# num=1: "Anlage A" #}

{# French (alias: alphabetique) #}
Annexe {{ num | alphabetique }}
{# num=1: "Annexe A" #}

{# Spanish (alias: alfabetico) #}
Anexo {{ num | alfabetico }}
{# num=1: "Anexo A" #}

Note: Values outside 1-26 are returned unchanged (no AA, AB, etc.).

Advanced Conditional Lists: {% enumerate %}

For creating a list of items where each item has its own inclusion condition, use the {% enumerate %} block tag. It automatically handles commas and conjunctions for a grammatically perfect result.

Inside the block, you define items using the {% item if ... %} tag:

Example: Paragraph Style

Screenshot

{# English #}
The limitation does not apply to {%
    enumerate conjunction="or" %}{%
    item if carveout['breach'] %}breach of confidentiality{% enditem %}{%
    item if carveout['infringement'] %}infringement of IPR{% enditem %}{%
    item if carveout['misconduct'] %}reckless conduct{% enditem %}{%
endenumerate %}.
{# All true: "breach of confidentiality, infringement of IPR, or reckless conduct." #}
{# First and last: "breach of confidentiality or reckless conduct." #}
{# First only: "breach of confidentiality." #}

{# Swedish (localized tags) #}
Ansvarsbegränsningen gäller inte {%
    upprakning konjunktion="eller" %}{%
    punkt om undantag['sekretess'] %}brott mot sekretess{% slut_punkt %}{%
    punkt om undantag['immaterialratt'] %}intrång i immateriella rättigheter{% slut_punkt %}{%
    punkt om undantag['grov_oaktsamhet'] %}grov oaktsamhet{% slut_punkt %}{%
slut_upprakning %}.

{# German (localized tags) #}
Die Haftungsbeschränkung gilt nicht für {%
    auflistung konjunktion="oder" %}{%
    punkt wenn ausnahme['vertraulichkeit'] %}Verletzung der Vertraulichkeit{% ende_punkt %}{%
    punkt wenn ausnahme['ip'] %}Verletzung von Schutzrechten{% ende_punkt %}{%
    punkt wenn ausnahme['fahrlassigkeit'] %}grobe Fahrlässigkeit{% ende_punkt %}{%
ende_auflistung %}.

{# French (localized tags) #}
La limitation ne s'applique pas à {%
    enumeration conjonction="ou" %}{%
    element si exception['confidentialite'] %}violation de la confidentialité{% fin_element %}{%
    element si exception['pi'] %}violation de la propriété intellectuelle{% fin_element %}{%
    element si exception['negligence'] %}faute lourde{% fin_element %}{%
fin_enumeration %}.

{# Spanish (localized tags) #}
La limitación no se aplica a {%
    enumeracion conjuncion="o" %}{%
    elemento si excepcion['confidencialidad'] %}incumplimiento de confidencialidad{% fin_elemento %}{%
    elemento si excepcion['pi'] %}infracción de PI{% fin_elemento %}{%
    elemento si excepcion['negligencia'] %}negligencia grave{% fin_elemento %}{%
fin_enumeracion %}.

Example: List Style

You can also generate a formatted <ul> list with correct end-of-line punctuation.

{# English #}
The limitation does not apply to:
{% enumerate style="list", conjunction="or" %}
  {% item if breach %}Breach of confidentiality{% enditem %}
  {% item if infringement %}Infringement of IPR{% enditem %}
{% endenumerate %}

{# Swedish #}
Ansvarsbegränsningen gäller inte:
{% upprakning stil="list", konjunktion="eller" %}
  {% punkt om sekretessbrott %}Brott mot sekretess{% slut_punkt %}
  {% punkt om immaterialrattsintrong %}Intrång i immateriella rättigheter{% slut_punkt %}
{% slut_upprakning %}

{# German #}
Die Haftungsbeschränkung gilt nicht für:
{% auflistung stil="list", konjunktion="oder" %}
  {% punkt wenn vertraulichkeit %}Verletzung der Vertraulichkeit{% ende_punkt %}
  {% punkt wenn schutzrechte %}Verletzung von Schutzrechten{% ende_punkt %}
{% ende_auflistung %}

{# French #}
La limitation ne s'applique pas à:
{% enumeration style="list", conjonction="ou" %}
  {% element si confidentialite %}Violation de la confidentialité{% fin_element %}
  {% element si propriete_intellectuelle %}Violation de la propriété intellectuelle{% fin_element %}
{% fin_enumeration %}

{# Spanish #}
La limitación no se aplica a:
{% enumeracion estilo="list", conjuncion="o" %}
  {% elemento si confidencialidad %}Incumplimiento de confidencialidad{% fin_elemento %}
  {% elemento si propiedad_intelectual %}Infracción de PI{% fin_elemento %}
{% fin_enumeracion %}

Possible Output (if both are true):

The limitation of liability does not apply to:

  • Breach of confidentiality; or
  • Infringement of IPR.

enumerate Tag Arguments

  • style (optional):
    • "paragraph" (default): Creates an inline, comma-separated list.
    • "list": Creates a <ul> bullet point list with correct punctuation.
  • conjunction (optional):
    • "and" (default): Uses the localized word for "and".
    • "or": Uses the localized word for "or".
    • Any other string: Uses the provided string verbatim (e.g., "och/eller").
  • transform (optional): Applies a filter to each item before joining.
    • "possessive": Applies the possessive filter to each item.
    • "capitalize": Capitalizes the first letter of each item.

Locale-Specific Aliases

To make templates more readable when written in a specific language, VIBE provides native-language aliases for filters and tags. The English (canonical) names always work, but you can use the aliases when your template locale matches.

Filter Aliases by Language

Filter (English) Swedish (sv) German (de) French (fr) Spanish (es)
plural plural plural pluriel plural
definite bestamd bestimmt defini definido
possessive genitiv genitiv possessif posesivo
indefinite obestamd unbestimmt indefini indefinido
cardinal kardinal kardinal cardinal cardinal
ordinal ordinal ordinal ordinal ordinal
doublet dublett wort_und_zahl doublet doblete
itemize upprakning auflistung enumerer enumerar
roman romersk romisch romain romano
alpha alfabetisk alphabetisch alphabetique alfabetico
strip_company_form utan_bolagsform

Tag Aliases by Language

The enumerate tag and its sub-tags also have localized aliases:

Tag (English) Swedish (sv) German (de) French (fr) Spanish (es)
enumerate upprakning auflistung enumeration enumeracion
endenumerate slut_upprakning ende_auflistung fin_enumeration fin_enumeracion
item punkt punkt element elemento
enditem slut_punkt ende_punkt fin_element fin_elemento

Tag Parameter Aliases

Parameters inside tags can also use localized names:

Parameter (English) Swedish (sv) German (de) French (fr) Spanish (es)
conjunction konjunktion konjunktion conjonction conjuncion
style stil stil style estilo
transform omvandla umwandeln transformer transformar
if om wenn si si

Note: The English names always work regardless of template locale. Aliases are purely for improved readability.

Filter Chaining

Filters can be chained together to apply multiple transformations:

{# English #}
{{ "renewal period" | plural | definite }}
{# Output: "the renewal periods" #}

{# Swedish #}
{{ "Förlängningsperiod" | plural | bestamd | genitiv }}
{# Output: "Förlängningsperiodernas" #}

{# German #}
{{ "Vertrag" | plural | bestimmt }}
{# Output: "die Verträge" #}

{# French #}
{{ "contrat" | pluriel | defini }}
{# Output: "les contrats" #}

{# Spanish #}
{{ "contrato" | plural | definido }}
{# Output: "los contratos" #}

Defined Terms

The defined-terms system highlights terms from a "Definitions" section in the live preview and optionally warns about missing or unused definitions. It uses the same linguistic engine described above to recognize inflected forms across all five supported locales.

For configuration keys, see Configuration Reference — Defined Terms.

How it works

  1. Extraction: VIBE scans the rendered Markdown for heading lines matching the configured heading (e.g., ## Definitions). Under each matching heading, paragraphs and list items that start with **bold text** are treated as definition entries. The bold text is the term; the full line is the definition.

  2. Tooltip injection: In the HTML preview, every occurrence of a defined term (including its inflected forms) is wrapped in a <span class="defined-term"> with a tooltip showing the definition. Occurrences inside <code>, <pre>, <a>, and inside the Definitions section itself are excluded.

  3. Validation (when validate: true):

    • Forward check — Scans the document body for title-cased mid-sentence words that look like defined terms but have no definition. Produces a warning for each undefined candidate.
    • Reverse check — Verifies that every term in the Definitions section actually appears somewhere in the document body. Any inflected form counts as a match. Produces a warning for unused terms.

Inflection rules by locale

Each locale generates a set of surface forms from the base term. For multi-word terms (e.g., "Service Level Agreement"), only the last word (head noun) inflects; the prefix is preserved verbatim.

Locale Forms generated
en base, plural, possessive ('s)
sv base, plural, definite singular, definite plural, possessive, +s
de base, genitive (+s only — plural is omitted to avoid false forms)
fr base, plural
es base, plural

When no language rules are available for a locale, only the base form is used.

German exception

All German nouns are capitalized, so the forward check (which looks for capitalized mid-sentence words) would produce excessive false positives. For locale: de:

  • The forward check is disabled — capitalized words in the body are never flagged as missing definitions.
  • The reverse check still runs — defined terms that don't appear in the document body are flagged.

The reverse check uses the German inflection rules (base + genitive) to match, so "Vertrag" in the Definitions section is satisfied by "Vertrags" appearing in the prose.

DOCX template support

For DOCX host templates, the extraction system works on Markdown-like text produced by the DOCX text extractor (extract_text_with_markers). Two prerequisites make this work:

  • Heading styles must map to Markdown headings. VIBE recognizes built-in Word heading styles for English, Swedish, German, French, and Spanish. For custom styles (e.g., "Beslutsmening"), add them via additional_word_headings in the config.
  • Bold formatting in DOCX runs is emitted as **bold** Markdown markers, so definition entries with bold terms are correctly extracted.

Whitelist

The whitelist config key accepts a list of strings (case-insensitive) that should be ignored by the forward check. Use this for proper nouns that are naturally capitalized mid-sentence but are not defined terms:

term_definitions:
  enable: true
  validate: true
  whitelist:
    - United Nations
    - European Union
    - Service Desk  # internal name, not a legal term

Components

When a template uses components ({{ insert() }}), the Definitions section may live in a component rather than the host template. During both preview rendering and startup validation, VIBE merges terms from the host and all components using host-first, first-occurrence-wins order. A term defined in the host is never overridden by the same term in a component.