oq-api
The experimental JavaScript API for oq’s Kalaallisut morphology and dictionary tools.
API contract ·
What’s new ·
Download package ·
Live import example
Usage
Noun number variants (sg/pl)
Some Kalaallisut noun roots do not disclose number on their own. For
example, qimmeq can mean either “a dog” or “dogs”, and
qitsuk can mean either “a cat” or “cats”. oq-api keeps both
presentation forms in the preset instead of guessing one.
The optional presentationVariants field is keyed by
language, number (sg/pl), and determination:
{
presentationVariants: {
countability: "count",
animacy: "animate",
gender: { da: "common" },
en: {
singular: { indefinite: "a dog", definite: "the dog" },
plural: { indefinite: "dogs", definite: "the dogs" },
},
da: {
singular: { indefinite: "en hund", definite: "hunden" },
plural: { indefinite: "hunde", definite: "hundene" },
},
},
}
morphemeEntryToPreset() preserves this metadata on the
preset and its sequence item. glossSummaryItems() accepts
numberPreference and determinationPreference,
returning the selected form as selectedPresentationVariant
while retaining all alternatives. The defaults are singular and
indefinite. Consumers can therefore render either
“I do not have a dog” or “I do not have dogs”, or show both when the
source does not resolve the number. countability is
available for future mass-noun rules.
Node.js REPL
Install the public release tarball from your shell, then start node and paste the JavaScript example below:
npm install https://jandahl.github.io/oq-api/downloads/oq-api-0.1-latest.tgz
const {
API_VERSION, analyzeWord, analyzeWordAsync,
GRAMMAR_MORPHEMES_URL, SCHEMA_MAJOR_VERSION, mergeMorphemeSources,
} = await import("oq-api");
const response = await fetch(GRAMMAR_MORPHEMES_URL);
const data = await response.json();
const { presets, anyOk } = mergeMorphemeSources(
[{ status: "fulfilled", value: data }],
[{ buildable: true, source: "grammarian", schemaMajorVersion: SCHEMA_MAJOR_VERSION }],
);
if (!anyOk) throw new Error("Could not load grammarian presets");
const { matches } = analyzeWord("qimmeqarpoq", presets);
console.log(API_VERSION, matches[0]?.confidence);
const controller = new AbortController();
const { matches: asyncMatches } = await analyzeWordAsync(
"qimmeqanngilanga", presets, {}, { signal: controller.signal },
);
console.log(asyncMatches.length);
Example output
0.1.1 exact
1
const { getWordClassColors } = await import("oq-api");
const colors = getWordClassColors(["nominal_root"]);
console.log(colors.fill);
For a script file, use a .mjs extension and a static import instead.
API examples
Each snippet uses the Node.js REPL import form. The examples below share this small, real grammarian-shaped fixture for qimmeqarpoq (“he/she/it has a dog”).
Shared example input
const {
buildWord, morphemeEntryToPreset, toBuilderItem,
} = await import("oq-api");
const rawEntries = [
{
id: "qimmeq",
lexical_facts: { morpheme_type: "stem", word_class: "N", meaning: "dog" },
application_logic: {
underlying_form: "qimmeq", continuation_class: "N_POSTBASE",
},
},
{
id: "N_qaq_Vb",
lexical_facts: {
morpheme_type: "derivational_affix",
category_shift: "N -> V", meaning: "to have",
},
application_logic: {
underlying_form: "-qaq", boundary_behavior: "truncating",
left_sandhi: "truncative", continuation_class: "V_POSTBASE",
},
},
{
id: "V_IND_INTR_3SG",
lexical_facts: {
morpheme_type: "inflectional_ending",
meaning: "indicative intransitive, 3sg",
},
inflection: {
mood: "indicative", transitivity: "intransitive",
subject: { person: 3, number: "sg" },
},
application_logic: {
underlying_form: "-voq", boundary_behavior: "additive",
continuation_class: "WORD_FINAL",
},
},
];
const presets = rawEntries.map(morphemeEntryToPreset);
const sequence = presets.map(({ seq }) => seq[0]);
const items = sequence.map(toBuilderItem);
const result = buildWord(sequence);
const rawWord = result.word;
Example output
sequence.map(({ id, text }) => ({ id, text }))
[
{ id: "qimmeq", text: "qimmeq" },
{ id: "N_qaq_Vb", text: "qaq" },
{ id: "V_IND_INTR_3SG", text: "voq" }
]
Build a word
Convert the shared raw entries to sequence items, then run the complete morphology pipeline:
const { buildWord } = await import("oq-api");
const result = buildWord(sequence);
console.log(result.word, result.approximate, result.closed);
Example output
qimmeqarpoq false true
Incremental engine
For an interactive builder, add already-converted sequence items one at a time:
const { WordBuilder, MORPH_OK } = await import("oq-api");
const builder = new WordBuilder();
builder.init();
for (const item of items) {
if (builder.add(item) !== MORPH_OK) throw new Error(builder.getError());
}
console.log(builder.getWord());
Example output
qimmeqqaqvoq
Word-class colors
Get stable colors without initializing the oq application:
const { getWordClassColors, WORD_CLASS_THEMES } = await import("oq-api");
const colors = getWordClassColors(["nominal_root"], WORD_CLASS_THEMES.light);
console.log(colors, WORD_CLASS_THEMES.light.name);
Example output
{
border: "hsl(220,55%,52%)",
fill: "hsl(220,30%,90%)",
text: "hsl(220,48%,30%)"
} light
Compose the morphology pipeline
Use the pure lower-level stages when your application needs its own pipeline:
const { applyAllomorphy, validateSequence, respellSurface } = await import("oq-api");
const grammar = validateSequence(sequence);
const surfaceItems = applyAllomorphy(sequence);
const displayWord = respellSurface(rawWord);
console.log(grammar.valid, surfaceItems, displayWord);
Example output
true [ /* allomorph-resolved items */ ] qimmeqarpoq
Deconstruct a word
Search verified morphology analyses, including cancellable asynchronous searches:
const { analyzeWord, analyzeWordAsync } = await import("oq-api");
const { matches } = analyzeWord("qimmeqarpoq", presets);
console.log(matches[0]?.confidence, matches[0]?.rankReasons);
const controller = new AbortController();
const { matches: asyncMatches } = await analyzeWordAsync(
"qimmeqarpoq", presets, {}, { signal: controller.signal },
);
// cacheAnalysisResult("qimmeqarpoq", asyncMatches);
console.log(asyncMatches.length);
Example output
exact [ "verified_rebuild", "grammarian_decomposition",
"shorter_morpheme_chain_preferred" ]
1
Analysis breakdown data
Use structured, DOM-free morpheme surface and breakdown information in another UI:
const { resolveMorphemeSurfaces, computeMorphemeBreakdownRows } = await import("oq-api");
const surfaces = resolveMorphemeSurfaces(sequence, result.word);
const rows = computeMorphemeBreakdownRows(items, result.word, sequence);
console.log(surfaces, rows);
Example output
surfaces.map(({ id, surfaceText }) => ({ id, surfaceText }))
[
{ id: "qimmeq", surfaceText: "qimme" },
{ id: "N_qaq_Vb", surfaceText: "qar" },
{ id: "V_IND_INTR_3SG", surfaceText: "poq" }
]
Morpheme data and glosses
Use structured presets, glosses, and source merging without depending on UI code:
const { morphemeEntryToPreset, glossSummaryItems } = await import("oq-api");
const preset = morphemeEntryToPreset(rawEntries[0]);
const glosses = glossSummaryItems(preset.seq);
console.log(preset.id, glosses[0]?.shortGloss);
Example output
root-noun "dog"
Labels and localization
Get display-ready labels in a selected locale:
const { setActiveLocale, resolveMoodLabel, resolvePersonLabel } = await import("oq-api");
setActiveLocale("en");
console.log(resolveMoodLabel("IND").text);
console.log(resolvePersonLabel(3, "SG"));
console.log(resolveMoodLabel("IND", { linguistTerms: true }).text);
Example output
Statement
he
Indicative
Dictionary lookup
Search the published upstream dictionaries with structured options:
const { searchEntries, DICT_SOURCES } = await import("oq-api");
const response = await searchEntries(DICT_SOURCES, "dog", {
lang: "en", match: "contains", rank: true,
});
console.log(response.results, response.errors);
Example output
[ /* matching entries */ ] []
Browser import
In a browser, import the same API from the public Pages distribution:
import { API_VERSION, getWordClassColors } from
"https://jandahl.github.io/oq-api/api/v0.1-latest/public-api.js";
console.log(API_VERSION,
getWordClassColors(["nominal_root"]));
Example output
0.1.1 {
border: "hsl(210,68%,50%)",
fill: "hsl(210,38%,9%)",
text: "hsl(210,52%,76%)"
}
Part of the oq family
The main Kalaallisut morphological dictionary and word-builder application.
A block-based learning aid for building and taking apart Kalaallisut words. It is a prototype, not an authoritative source.
A related interactive project by the same author.
Public API URLs
v0.1-latest is a copy of the newest 0.1.x. Downstream can keep
that URL until a breaking 0.2.x. Frozen v0.1.N
snapshots stay up as well.
Frozen patches
Release tarballs are built from tags, tested in GitHub Actions, and carry provenance attestations.