Skip to content

Schema Overview

ODict dictionaries are authored in XML using the ODXML (Open Dictionary XML) format. This page provides a conceptual overview of how the schema is structured, from minimal dictionary entries to richer lexical data such as ranks, pronunciations, media, forms, tags, translations, lemmas, notes, and examples. For the element-by-element reference, see the Schema Reference.

An ODXML file describes a dictionary as a hierarchy:

flowchart TB
  dictionary["`<dictionary>`"]
  entry["`<entry>`"]
  pronunciation["`<pronunciation>`"]
  ety["`<ety>`"]
  sense["`<sense>`"]
  form["`<form>`"]
  tag["`<tag>`"]
  translation["`<translation>`"]
  group["`<group>`"]
  groupedDefinition["`<definition>`"]
  definition["`<definition>`"]
  example["`<example>`"]
  note["`<note>`"]
  noteExample["`<example>`"]

  dictionary --> entry
  entry --> ety
  entry --> media["`<media>`"]
  ety --> pronunciation
  ety --> sense
  sense --> form
  sense --> tag
  sense --> translation
  sense --> group
  sense --> definition
  group --> groupedDefinition
  groupedDefinition --> example
  groupedDefinition --> note
  definition --> example
  definition --> note
  note --> noteExample

The simplest valid dictionary:

<dictionary>
<entry term="hello">
<ety>
<sense pos="intj">
<definition value="A greeting" />
</sense>
</ety>
</entry>
</dictionary>

Each <entry> represents a headword. Entries can either contain full definitions (via <ety> children) or redirect to another entry using the see attribute:

<entry term="run">
<ety>
<sense pos="v">
<definition value="To move swiftly on foot" />
</sense>
</ety>
</entry>
<!-- "ran" redirects to "run" -->
<entry term="ran" see="run" />

When looking up “ran” with the follow option enabled, ODict will resolve the cross-reference and return the “run” entry.

Use rank on an entry when you have frequency data. Lower or higher rank semantics are up to the source dictionary, but ODict exposes min_rank and max_rank in the language bindings so applications can normalize the values.

<entry term="run" rank="100">
<ety>
<sense pos="v">
<definition value="To move swiftly on foot" />
</sense>
</ety>
</entry>

If a word has multiple distinct origins, you can define multiple <ety> elements:

<entry term="bank">
<ety description="From Italian banca (bench)">
<sense pos="n">
<definition value="A financial institution" />
</sense>
</ety>
<ety description="From Old Norse bakki">
<sense pos="n">
<definition value="The land alongside a river" />
</sense>
</ety>
</entry>

Within an etymology, <sense> elements group definitions by part of speech. The pos attribute accepts standard codes like n (noun), v (verb), adj (adjective), etc. See the reference for the full list.

<sense pos="n">
<definition value="An animal" />
</sense>
<sense pos="v">
<definition value="To follow persistently" />
</sense>

If the part of speech is unknown or not applicable, you can omit pos entirely.

Use the optional lemma attribute when a sense represents an inflected or otherwise derived form of another headword. The value is an entry reference: it should match the term of the canonical <entry>.

<entry term="running">
<ety>
<sense pos="v" lemma="run">
<definition value="The present participle of run" />
</sense>
</ety>
</entry>

Lemmas belong to senses rather than entries because a headword can combine unrelated grammatical meanings. For example, the verb sense of “saw” can use lemma="see" while the noun sense remains under “saw” without a lemma reference.

The reference is exposed as part of the sense data; it does not make lookup automatically return the lemma entry. This differs from an entry’s see attribute, which redirects lookup when redirect following is enabled. A <form> records the inverse relationship from a lemma to one of its forms. See the <sense> reference for a complete example.

Use <form> to describe inflections, conjugations, plurals, comparatives, and other related forms. Forms belong to a sense so homographs and words with multiple parts of speech can each have the correct set:

<entry term="run">
<ety>
<sense pos="v">
<form kind="conjugation" term="ran" />
<form kind="conjugation" term="running" />
<definition value="To move swiftly on foot" />
</sense>
</ety>
</entry>

Forms can also carry tags:

<form kind="plural" term="words">
<tag>archaic</tag>
</form>

The standard form kinds are conjugation, inflection, plural, singular, comparative, and superlative. You can also use a custom value when the standard kinds do not fit your source data.

Tags describe usage labels such as register, dialect, domain, or grammar notes:

<sense pos="n">
<tag>informal</tag>
<tag>slang</tag>
<definition value="A person, especially a man" />
</sense>

Translations can appear on senses and examples:

<sense pos="intj">
<translation lang="es" value="hola" />
<translation lang="fr" value="bonjour" />
<definition value="A greeting">
<example value="Hello, world!">
<translation lang="es" value="Hola, mundo!" />
</example>
</definition>
</sense>

When a sense has many definitions, you can organize them with <group>:

<sense pos="v">
<group description="Motion senses">
<definition value="To move swiftly on foot" />
<definition value="To flee" />
</group>
<group description="Operational senses">
<definition value="To operate or manage" />
<definition value="To execute a program" />
</group>
</sense>

Definitions can have <example> and <note> children. Use notes for extra context tied to a definition:

<definition value="A small domesticated mammal">
<example value="The cat sat on the mat." />
<example value="She adopted two cats." />
<note value="Informal usage can also refer to a person">
<example value="He's a cool cat." />
</note>
</definition>

Pronunciations can be attached to etymologies and examples. This allows entries with distinct origins to carry different pronunciations, while examples can describe how a complete phrase is spoken. The kind attribute is optional and supports standard values such as ipa, pinyin, hiragana, and romaji, as well as custom strings.

<entry term="hello">
<ety>
<pronunciation kind="ipa" value="həˈləʊ">
<url src="./audio/hello_uk.mp3" type="audio/mpeg" description="British" />
</pronunciation>
<pronunciation kind="ipa" value="hɛˈloʊ">
<url src="./audio/hello_us.mp3" type="audio/mpeg" description="American" />
</pronunciation>
<sense pos="intj">
<definition value="A greeting">
<example value="Hello, how are you?">
<pronunciation kind="ipa" value="həˈləʊ haʊ ɑː juː" />
</example>
</definition>
</sense>
</ety>
<media src="./images/hello.svg" type="image/svg+xml" />
</entry>

Entry-level <media> elements can point to images, audio, video, or other resources. Both <media> and the <url> children of pronunciations accept absolute HTTP(S) URLs or relative paths beginning with ./ or /.

Pronunciation kinds are especially useful for non-Latin scripts:

<entry term="你好">
<ety>
<pronunciation kind="pinyin" value="nǐ hǎo" />
<pronunciation kind="ipa" value="ni˨˩ xɑʊ̯˧˥" />
...
</ety>
</entry>

The schema is formally defined in odict.xsd. You can validate your XML against it:

<?xml version="1.0" encoding="UTF-8"?>
<dictionary name="My Dictionary"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://odict.org/odict.xsd">
...
</dictionary>

This schema is included by default when scaffolding dictionaries with odict new. Most XML editors (VS Code with the XML extension, IntelliJ, etc.) will provide autocomplete and validation when the XSD is referenced.