@heroiclands/package-build 23.5.0 → 23.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/bin/content-commands.mjs +23 -5
  3. package/bin/package-build.mjs +1 -74
  4. package/docs/api.md +9 -0
  5. package/docs/authoring/links-and-markup.md +77 -12
  6. package/docs/commands.md +4 -48
  7. package/docs/content-format.md +16 -0
  8. package/docs/diagnostics.md +1 -2
  9. package/docs/guides/module-project.md +1 -1
  10. package/docs/guides/reference-pages.md +89 -0
  11. package/docs/guides/release-and-deploy.md +3 -3
  12. package/docs/project-setup.md +16 -59
  13. package/docs/reference/format-details.md +533 -84
  14. package/docs/reference/note-types.md +191 -149
  15. package/engine/base-compiler.mjs +21 -0
  16. package/engine/cdn-publish.mjs +30 -5
  17. package/engine/content-format.yaml +9 -0
  18. package/engine/content-index.mjs +19 -5
  19. package/engine/culture-reach.mjs +330 -0
  20. package/engine/dictionary-layout.mjs +340 -0
  21. package/engine/dictionary-rows.mjs +608 -0
  22. package/engine/glossary-entries.mjs +182 -0
  23. package/engine/glossary-language.mjs +99 -0
  24. package/engine/index.mjs +1 -0
  25. package/engine/infobox.mjs +8 -0
  26. package/engine/language-words.mjs +360 -0
  27. package/engine/literature-notes.mjs +10 -8
  28. package/engine/note-addresses.mjs +25 -0
  29. package/engine/note-vocabulary.mjs +161 -1
  30. package/engine/path-charset.mjs +120 -0
  31. package/engine/project-init.mjs +4 -29
  32. package/engine/sql-tables.mjs +311 -21
  33. package/engine/word-classes.mjs +167 -0
  34. package/engine/word-rows.mjs +233 -0
  35. package/engine/word-terms.mjs +306 -0
  36. package/package.json +2 -4
  37. package/types/engine/cdn-publish.d.mts +26 -25
  38. package/types/engine/content-index.d.mts +15 -5
  39. package/types/engine/culture-reach.d.mts +66 -0
  40. package/types/engine/dictionary-layout.d.mts +69 -0
  41. package/types/engine/dictionary-rows.d.mts +39 -0
  42. package/types/engine/glossary-entries.d.mts +12 -0
  43. package/types/engine/glossary-language.d.mts +42 -0
  44. package/types/engine/index.d.mts +1 -0
  45. package/types/engine/language-words.d.mts +33 -0
  46. package/types/engine/literature-notes.d.mts +2 -2
  47. package/types/engine/note-addresses.d.mts +17 -0
  48. package/types/engine/path-charset.d.mts +38 -0
  49. package/types/engine/sql-tables.d.mts +7 -2
  50. package/types/engine/word-classes.d.mts +87 -0
  51. package/types/engine/word-rows.d.mts +64 -0
  52. package/types/engine/word-terms.d.mts +68 -0
  53. package/labels.mjs +0 -191
  54. package/types/labels.d.mts +0 -40
@@ -2810,6 +2810,45 @@ stays a list: `list_contains("where".locus, 'thalorna-note-place-ironfells')`.
2810
2810
  quotes, `"when"` and `"where"`. A note tagged `gm` contributes no row on a
2811
2811
  public surface.
2812
2812
 
2813
+ **`words`** is one row per word — per entry of `data.words` on every
2814
+ [language note](#a-languages-words), stubs included — in the order the note
2815
+ states them:
2816
+
2817
+ | Column | What it holds |
2818
+ | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
2819
+ | `language` | The Address of the language note the word is written on, as another note names it: `thalorna-note-skill-provnzlng`. |
2820
+ | `position` | Its place in the note's `data.words`, from `0`. `ORDER BY position` is the note's own order. |
2821
+ | `form`, `class`, `gloss`, … | Every key of a word, each its own column under its own name. `name_sort` reads as the form where the word states none. |
2822
+ | `built_text` | The `built` pieces joined with `+`, as `wulf- + -rith`; `NULL` for a root word. |
2823
+ | `built_parts` | The pieces as a list of `{ form, gloss }`, each glossed from a word or affix of the same language, with a `NULL` gloss where none is. |
2824
+
2825
+ A key no word writes is still a column, `NULL` in every row, and a word of a
2826
+ language note tagged `gm` contributes no row on a public surface. A row holds
2827
+ the words its own note states, and nothing more: a daughter language's
2828
+ inherited words are read through `language_lineage`.
2829
+
2830
+ **`language_lineage`** is one row per language note and each language it
2831
+ inherits words from through [`data.family`](#a-languages-words): `language`,
2832
+ `ancestor` and `depth`, with the language itself at depth `0`, its parent at
2833
+ `1`, and so on up the chain. Joining `words` on `words.language =
2834
+ language_lineage.ancestor` gives a language's words with its inherited ones,
2835
+ and `depth` says how far up each was found. A chain stops at a parent that is
2836
+ no language note of the same package.
2837
+
2838
+ **`glossary`** is one row per entry of a culture's or an affiliation's
2839
+ [`data.glossary`](#glossary-terms): `note` (the Address of the culture or
2840
+ affiliation), `position`, and the entry's `name`, `name_sort` (the name where
2841
+ the entry states none), `common` (`false` where it states none), `definition`,
2842
+ `pronunciation` and `language`. `language` is the language the entry
2843
+ resolves to, its own or the one its note gives it, and `NULL` only where it
2844
+ resolves to none, which the lint refuses.
2845
+
2846
+ **`dictionary`** is one row per word per language it is listed under,
2847
+ inherited words included, and **`terms`** one row per glossary term, rank or
2848
+ office per note that owns it. See
2849
+ [dictionaries and glossaries](#dictionaries-and-glossaries) for their columns
2850
+ and how their rows are built.
2851
+
2813
2852
  **`market`** is the six-step market scale as a relation — `value`, `name`,
2814
2853
  `trade` — so a table prints `village` beside the number a note wrote without a
2815
2854
  second copy of the scale living in authored content.
@@ -4627,36 +4666,44 @@ rank names the standing, and the standing says.
4627
4666
 
4628
4667
  **Standing**: aligned, unaligned, rival, nemesis
4629
4668
 
4630
- | `data` property | Values | Description |
4631
- | ----------------------------------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
4632
- | `templatePriority` | `number` | Template priority, _null_ = not a template |
4633
- | `events` | event entries — see [Events](#events) | What happened to or at this subject: dated, attributed events |
4634
- | `demonym` | `string` | What a member of this affiliation is called (a Vylarian) |
4635
- | `epithet` | `string` | The by-name it is known by — a god's, an order's, a company's |
4636
- | `symbol` | `string` | Its emblem in words: a feather atop a golden scale, a chisel carving a star |
4637
- | `governance.model` | `GovernanceModel` | Type of government structure, if applicable |
4638
- | `governance.summary` | `string` | summary of the governance situation |
4639
- | `governance.ranks` | `Rank[]` | The ranks the affiliation confers; level 0 and level 1 are required |
4640
- | `governance.ranks[].level` | `integer`, required | The rung's position on this body's ladder; a member's `rank` names it |
4641
- | `governance.ranks[].title` | `string`, required | What the standing is called |
4642
- | `governance.ranks[].description` | `string`, required | What the standing is |
4643
- | `governance.ranks[].lore` | `Address` | The `law` lore note stating what the standing is, shared with every body that confers it |
4644
- | `governance.offices` | `Map<name, description \| {description, holders}>` | Official offices and their dated holders |
4645
- | `governance.offices.<name>` | `string` or `{ description, holders }` | One office, keyed by its name: its description, or a map of description and holders |
4646
- | `governance.offices.<name>.description` | `string`, required | What the office is |
4647
- | `governance.offices.<name>.holders` | `{ being, start?, end?, contested? }[]`, required | Who has held the office, each with the dates of the term |
4648
- | `governance.offices.<name>.holders[].being` | `Address`, required | The `being` who held it |
4649
- | `governance.offices.<name>.holders[].start` | `date` | When the term began; omitted, it began before anything recorded |
4650
- | `governance.offices.<name>.holders[].end` | `date` | When the term ended; omitted, the being holds the office now |
4651
- | `governance.offices.<name>.holders[].contested` | `boolean` | `true` where the term may overlap another holder's |
4652
- | `seat` | `Address` | Where the affiliation's authority sits |
4653
- | `population` | `number` | Number of people in the affiliation (precision 2 significant digits). |
4654
- | `economy` | `Address[]` | Economic life: `affiliation` or `lore` Addresses, with an explicit type and no default |
4655
- | `lore` | `Address[]` | Lore concerning it — the peoples it draws on, the god a faith venerates, its law, its calendar |
4656
- | `parents` | `Address[]` | Affiliations that this affiliation is subordinate to |
4657
- | `culture` | `Address` | The people this affiliation belongs to, naming a lore note with `subType: culture` |
4658
- | `relations` | `Map<Address, Standing>` | Standing with other affiliations, keyed by the other body's Address |
4659
- | `relations.<Address>` | `aligned`, `unaligned`, `rival` or `nemesis` | The standing this body holds toward that one; any other value is an error at the value |
4669
+ | `data` property | Values | Description |
4670
+ | ----------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
4671
+ | `templatePriority` | `number` | Template priority, _null_ = not a template |
4672
+ | `events` | event entries — see [Events](#events) | What happened to or at this subject: dated, attributed events |
4673
+ | `demonym` | `string` | What a member of this affiliation is called (a Vylarian) |
4674
+ | `epithet` | `string` | The by-name it is known by — a god's, an order's, a company's |
4675
+ | `symbol` | `string` | Its emblem in words: a feather atop a golden scale, a chisel carving a star |
4676
+ | `governance.model` | `GovernanceModel` | Type of government structure, if applicable |
4677
+ | `governance.summary` | `string` | summary of the governance situation |
4678
+ | `governance.ranks` | `Rank[]` | The ranks the affiliation confers; level 0 and level 1 are required |
4679
+ | `governance.ranks[].level` | `integer`, required | The rung's position on this body's ladder; a member's `rank` names it |
4680
+ | `governance.ranks[].title` | `string`, required | What the standing is called |
4681
+ | `governance.ranks[].description` | `string`, required | What the standing is |
4682
+ | `governance.ranks[].lore` | `Address` | The `law` lore note stating what the standing is, shared with every body that confers it |
4683
+ | `governance.offices` | `Map<name, description \| {description, holders}>` | Official offices and their dated holders |
4684
+ | `governance.offices.<name>` | `string` or `{ description, holders }` | One office, keyed by its name: its description, or a map of description and holders |
4685
+ | `governance.offices.<name>.description` | `string`, required | What the office is |
4686
+ | `governance.offices.<name>.holders` | `{ being, start?, end?, contested? }[]`, required | Who has held the office, each with the dates of the term |
4687
+ | `governance.offices.<name>.holders[].being` | `Address`, required | The `being` who held it |
4688
+ | `governance.offices.<name>.holders[].start` | `date` | When the term began; omitted, it began before anything recorded |
4689
+ | `governance.offices.<name>.holders[].end` | `date` | When the term ended; omitted, the being holds the office now |
4690
+ | `governance.offices.<name>.holders[].contested` | `boolean` | `true` where the term may overlap another holder's |
4691
+ | `seat` | `Address` | Where the affiliation's authority sits |
4692
+ | `population` | `number` | Number of people in the affiliation (precision 2 significant digits). |
4693
+ | `economy` | `Address[]` | Economic life: `affiliation` or `lore` Addresses, with an explicit type and no default |
4694
+ | `lore` | `Address[]` | Lore concerning it — the peoples it draws on, the god a faith venerates, its law, its calendar |
4695
+ | `parents` | `Address[]` | Affiliations that this affiliation is subordinate to |
4696
+ | `culture` | `Address` | The people this affiliation belongs to, naming a lore note with `subType: culture` |
4697
+ | `language` | `Address` | The trade, temple or order tongue it keeps, naming a skill note with `subType: language` |
4698
+ | `glossary` | `{ name, name_sort?, common?, definition?, pronunciation?, language? }[]` | Its own glossary terms — see [glossary terms](#glossary-terms) |
4699
+ | `glossary[].name` | `string`, required | The headword |
4700
+ | `glossary[].name_sort` | `string` | Its sort key; the name when unstated |
4701
+ | `glossary[].common` | `boolean` | `true` for a word a player is expected to know by heart; `false` when unstated |
4702
+ | `glossary[].definition` | `string` | The sense text; stated, it creates the term or replaces every sense its other sources give |
4703
+ | `glossary[].pronunciation` | `string` | A respelling, as `sol-MEH-reh`: letters, apostrophes and hyphens, at most one syllable in capitals |
4704
+ | `glossary[].language` | `Address` | The language the term is in, naming a skill note with `subType: language`; unstated, the affiliation's, its parents' or its culture's |
4705
+ | `relations` | `Map<Address, Standing>` | Standing with other affiliations, keyed by the other body's Address |
4706
+ | `relations.<Address>` | `aligned`, `unaligned`, `rival` or `nemesis` | The standing this body holds toward that one; any other value is an error at the value |
4660
4707
 
4661
4708
  **An affiliation names the people it belongs to in `data.culture`.** An
4662
4709
  affiliation that names none takes its culture from its `data.parents`. A
@@ -4664,6 +4711,21 @@ affiliation that names none takes its culture from its `data.parents`. A
4664
4711
  together with every place whose `data.government` names it. See
4665
4712
  [Culture in the index](#culture-in-the-index).
4666
4713
 
4714
+ **An affiliation may keep a tongue of its own.** A trade cant, a temple
4715
+ language or an order's tongue is named in the affiliation's `data.language`, and
4716
+ it reaches the affiliation's culture: its words belong to that culture's
4717
+ glossary as much as the words of the tongue the culture names itself. It is
4718
+ also the language of the affiliation's own [glossary terms](#glossary-terms)
4719
+ where an entry names none. In a tree that holds at least one culture note, an
4720
+ affiliation that reaches no culture — naming none, inheriting none through
4721
+ `data.parents`, and, for a `polity`, taking none from its places — is a warning
4722
+ at its `type:` line, because its ranks, offices and tongue enter no culture's
4723
+ glossary.
4724
+
4725
+ A language needs no culture and no affiliation. A dead language, or one kept
4726
+ only by a church, is a language note like any other, and nothing requires a
4727
+ culture or an affiliation to name it.
4728
+
4667
4729
  **A faith tradition is not its god.** An `affiliation` of subType
4668
4730
  `faithtradition` is a _religion_ — a practice, with an ordained priesthood, a
4669
4731
  calendar and a body of observance — and it can outlive belief in the god
@@ -5140,16 +5202,166 @@ SoHL sets `system.quantity` to `1` for each compendium item.
5140
5202
  - combat
5141
5203
  - combattechnique
5142
5204
 
5143
- | `data` property | Values | Description |
5144
- | -------------------- | ----------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
5145
- | `templatePriority` | `number` | Template priority, _null_ = not a template |
5146
- | `masteryLevel` | `number` | HM3 mastery level |
5147
- | `parentSkill` | `Address` | Parent skill this skill specializes |
5148
- | `names` | `{ pattern, given, <name>... }` | `language` only: how its speakers are named, for `package-build npc new` — see [name lists](#name-lists) |
5149
- | `names.pattern` | `string`, `string[]` or map of `gender` words to either, required | Placeholders such as `{given}`, `{given.male}` and `{clan}`, each naming a list, with literal text around them; a list states several forms, a map each gender's forms |
5150
- | `names.given` | map of `gender` words to lists of names, required | Given names by gender; `female` and `male` are required |
5151
- | `names.given.<name>` | `string[]` | The given names of one gender |
5152
- | `names.<name>` | `string[]` | A list the placeholder of the same name draws from |
5205
+ | `data` property | Values | Description |
5206
+ | ------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
5207
+ | `templatePriority` | `number` | Template priority, _null_ = not a template |
5208
+ | `masteryLevel` | `number` | HM3 mastery level |
5209
+ | `parentSkill` | `Address` | Parent skill this skill specializes |
5210
+ | `names` | `{ pattern, given, <name>... }` | `language` only: how its speakers are named, for `package-build npc new` — see [name lists](#name-lists) |
5211
+ | `names.pattern` | `string`, `string[]` or map of `gender` words to either, required | Placeholders such as `{given}`, `{given.male}` and `{clan}`, each naming a list, with literal text around them; a list states several forms, a map each gender's forms |
5212
+ | `names.given` | map of `gender` words to lists of names, required | Given names by gender; `female` and `male` are required |
5213
+ | `names.given.<name>` | `string[]` | The given names of one gender |
5214
+ | `names.<name>` | `string[]` | A list the placeholder of the same name draws from |
5215
+ | `family` | `Address` | `language` only: the parent language, naming a skill note with `subType: language`; this language reads the parent's words as its own — see [a language's words](#a-languages-words) |
5216
+ | `abbreviation` | `string` | `language` only: the tag a dictionary prints for it, as `Prov.`; its full name where unstated — see [dictionaries and glossaries](#dictionaries-and-glossaries) |
5217
+ | `classModifiers` | `string[]` | `language` only: the word class modifiers the language uses, each from the [master word classes](#word-classes); empty when unstated |
5218
+ | `fields` | `{ name, holds }[]` | `language` only: the fields its words are grouped under |
5219
+ | `fields[].name` | `string`, required | The field's name, which a word's `field` names |
5220
+ | `fields[].holds` | `string`, required | What the field holds |
5221
+ | `words` | `{ form, class, field, gloss, … }[]` | `language` only: its words, one map each, in the order they print — see [a language's words](#a-languages-words) |
5222
+ | `words[].form` | `string`, required | The word as cited, bare: no backticks |
5223
+ | `words[].name_sort` | `string` | Its sort key; the form when unstated |
5224
+ | `words[].class` | `string`, required | A class from the master word classes, base first: `n I f` |
5225
+ | `words[].field` | `string`, required | One of the note's `fields` names |
5226
+ | `words[].gloss` | `string`, required | The meaning, in brief |
5227
+ | `words[].source` | `{ kind, language?, form? }` | Where the word comes from |
5228
+ | `words[].source.kind` | `inherited`, `borrowed`, `kept`, `worn`, `coined`, `substrate` or `island`, required | How the word came into the language |
5229
+ | `words[].source.language` | `Address` | The language a `borrowed` word came from, naming a skill note with `subType: language`; required for `borrowed` |
5230
+ | `words[].source.form` | `string` | The form the word had in its source, bare |
5231
+ | `words[].built` | `string[]` | The pieces the word is built from, in order, as the note's words and affixes write them; absent for a root word |
5232
+ | `words[].attested` | `Address[]` | The notes the word is cited from |
5233
+ | `words[].pronunciation` | `string` | A respelling, as `sol-MEH-reh`: letters, apostrophes and hyphens, at most one syllable in capitals |
5234
+ | `words[].tones` | `string` | A tone pattern, as `LHL` |
5235
+ | `words[].root` | `string` | A consonantal skeleton, as `d-l-k` |
5236
+ | `words[].frame` | `string` | The frame a root is set in |
5237
+ | `affixes` | `{ form, gloss, attaches?, examples? }[]` | `language` only: the affixes a word's `built` pieces may name |
5238
+ | `affixes[].form` | `string`, required | The affix as a `built` piece names it, as `-rith` |
5239
+ | `affixes[].gloss` | `string`, required | What it means |
5240
+ | `affixes[].attaches` | `string` | How it attaches, as `replaces a closing vowel` |
5241
+ | `affixes[].examples` | `string[]` | The names it is read from |
5242
+
5243
+ #### A language's words
5244
+
5245
+ A language note states its words in frontmatter, beside its
5246
+ [name lists](#name-lists), so every surface reads them from the content index
5247
+ rather than from a table in the body. Each word is one flow-style line of
5248
+ `data.words`, and the language of a word is the note it sits on:
5249
+
5250
+ ```yaml
5251
+ ---
5252
+ shortcode: provnzlng
5253
+ name: { full: Provenzal Language }
5254
+ type: skill
5255
+ subType: language
5256
+ data:
5257
+ classModifiers: [f, m]
5258
+ fields:
5259
+ - { name: Love and its conduct, holds: "Longing, courtship and the courts of love." }
5260
+ affixes:
5261
+ - { form: -ère, gloss: "one who", attaches: replaces a closing vowel }
5262
+ words:
5263
+ - {
5264
+ form: solmère,
5265
+ class: n m,
5266
+ field: Love and its conduct,
5267
+ gloss: "longing; the ache for a person, a place or a time that is gone",
5268
+ source: { kind: borrowed, language: skill-elvndrlng, form: sólmerë },
5269
+ attested: [lore-lastshrine],
5270
+ }
5271
+ - {
5272
+ form: amère,
5273
+ class: n f,
5274
+ field: Love and its conduct,
5275
+ gloss: "a beloved",
5276
+ built: [am-, -ère],
5277
+ }
5278
+ ---
5279
+ ```
5280
+
5281
+ `data.*` never reaches the compiled Item: the Item takes only the `sohl` block,
5282
+ so a language's words, however many, add nothing to it.
5283
+
5284
+ **A class is a base and its modifiers**, from the
5285
+ [master word classes](#word-classes), separated by spaces: `n I f` is a noun of
5286
+ class I, feminine. A language declares the modifiers it uses in
5287
+ `data.classModifiers`, and a word may carry only those.
5288
+
5289
+ **A word's source** is closed to seven kinds. `borrowed` names the language the
5290
+ word came from in `language`, a language Address; `inherited`, `kept` and
5291
+ `worn` give the ancient `form` where the note knows it; `coined`, `substrate`
5292
+ and `island` give neither. A form is written bare: a renderer supplies its own
5293
+ italics and its own asterisk.
5294
+
5295
+ **`built` names the pieces a word is made of**, each as a word or an affix of
5296
+ the same language writes it, stems included. A piece written with different
5297
+ joining hyphens still matches (`am-` finds the word `am`).
5298
+
5299
+ **A daughter language names its parent in `data.family`**, and reads the
5300
+ parent's words as its own, and the parent's parent's in turn. The words a
5301
+ family shares sit on the parent's note once; each daughter states only its own:
5302
+
5303
+ ```yaml
5304
+ # Skills/Languages/Byzarian.md — a daughter of Helonic
5305
+ type: skill
5306
+ subType: language
5307
+ data:
5308
+ family: skill-helonclng
5309
+ words:
5310
+ - { form: kyma, class: n, field: Sea, gloss: "a wave" }
5311
+ ```
5312
+
5313
+ **`data.abbreviation` is the tag a dictionary prints for the language**,
5314
+ beside an entry's class and in an etymology: `Prov.` for Provenzal. A language
5315
+ that states none is tagged with its full name. It is stated only on a language
5316
+ note, and a blank value is an error.
5317
+
5318
+ `data.family` is stated only on a language note, and names a language note.
5319
+ Following it from note to note never returns to where it started: a chain
5320
+ that does is a cycle, and an error on every note in it.
5321
+
5322
+ Each finding is located at the key at fault:
5323
+
5324
+ | Finding | Severity |
5325
+ | --------------------------------------------------------------------- | -------- |
5326
+ | A word key or `data.family` on a skill of another subType, at the key | error |
5327
+ | A `data.family` naming no language note, at the value | error |
5328
+ | A `data.family` chain that returns to its start, at the value | error |
5329
+ | An unknown, missing or wrongly shaped key in an entry | error |
5330
+ | A class whose base or modifier is not in the master set | error |
5331
+ | A modifier `data.classModifiers` does not declare | error |
5332
+ | A declared modifier no word uses, at the modifier | warning |
5333
+ | A `field` not among `data.fields` | error |
5334
+ | A `borrowed` source with no `language` | error |
5335
+ | A form standing twice in one language, at the second | error |
5336
+ | An `attested` or language Address that does not resolve | error |
5337
+ | A language Address naming something other than a language skill | error |
5338
+ | A pronunciation that is not a respelling | error |
5339
+ | A `built` piece no word or affix of the language glosses | warning |
5340
+
5341
+ The `words` relation holds one row per word — see
5342
+ [Content tables](#content-tables) — and a table of them is an `sql` fence over
5343
+ it. A dictionary reads [the `dictionary` relation](#dictionaries-and-glossaries) instead. A daughter's words with its inherited ones join `words` to
5344
+ `language_lineage`:
5345
+
5346
+ ````markdown
5347
+ ```sql
5348
+ SELECT w.form AS "Form", w.gloss AS "Gloss"
5349
+ FROM words w JOIN language_lineage l ON w.language = l.ancestor
5350
+ WHERE l.language = 'thalorna-note-skill-byzarnlng'
5351
+ ORDER BY w.name_sort
5352
+ ```
5353
+ ````
5354
+
5355
+ A single language's own words are a fence over `words` alone:
5356
+
5357
+ ````markdown
5358
+ ```sql
5359
+ SELECT form AS "Form", class AS "Class", gloss AS "Gloss", built_text AS "Built from"
5360
+ FROM words
5361
+ WHERE language = 'thalorna-note-skill-provnzlng' AND field = 'Love and its conduct'
5362
+ ORDER BY position
5363
+ ```
5364
+ ````
5153
5365
 
5154
5366
  if a `sohl` property is present, a SoHL item of type "skill" is created.
5155
5367
 
@@ -5317,53 +5529,239 @@ not a different cultural identity. See
5317
5529
  [Culture parents](#culture-parents-and-inherited-fields) and
5318
5530
  [Culture in the index](#culture-in-the-index).
5319
5531
 
5320
- | `data` property | Values | Description |
5321
- | ------------------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
5322
- | `epoch` | canonical `<year>.<day>` | The canonical day that equals calendar year 1, day 1 |
5323
- | `months` | `{ name, abbreviation?, days }[]` | Ordered months; their days sum to the world's year |
5324
- | `months[].name` | `string`, required | The month's name |
5325
- | `months[].abbreviation` | `string` | Its short form, for a compact date |
5326
- | `months[].days` | `integer`, required | How many days the month holds |
5327
- | `weekdays` | `{ name, abbreviation? }[]` | Ordered week days, indexed from zero; weeks run continuously |
5328
- | `weekdays[].name` | `string`, required | The day's name |
5329
- | `weekdays[].abbreviation` | `string` | Its short form, for a compact date |
5330
- | `seasons` | `{ name, abbreviation?, start }[]` | Seasons starting on one-based days of year |
5331
- | `seasons[].name` | `string`, required | The season's name |
5332
- | `seasons[].abbreviation` | `string` | Its short form, for a compact date |
5333
- | `seasons[].start` | `integer`, required | The one-based day of the year it begins; starts increase down the list |
5334
- | `namedDays` | `{ name, abbreviation?, day }[]` | Names assigned to one-based days of year |
5335
- | `namedDays[].name` | `string`, required | The day's name |
5336
- | `namedDays[].abbreviation` | `string` | Its short form, for a compact date |
5337
- | `namedDays[].day` | `integer`, required | The one-based day of the year it names, distinct within the calendar |
5338
- | `eras` | `{ shortcode, name, abbreviation?, marker?, proclaimedBy?, start, label? }[]` | Year counts; `start` is `null` or an in-calendar year |
5339
- | `eras[].shortcode` | `string`, required | The era's own segment of `<calendar>.<era>`, unique within the calendar |
5340
- | `eras[].name` | `string`, required | The era's name |
5341
- | `eras[].marker` | `string` | Uppercase letters and digits, beginning with a letter, unique across the corpus; an authored date names the era by it |
5342
- | `eras[].abbreviation` | `string` | Its short form, for a compact date |
5343
- | `eras[].proclaimedBy` | `Address` | The body that began the reckoning |
5344
- | `eras[].start` | `null` or `integer`, required | The calendar year the era begins; `null` for the era before year 1 |
5345
- | `eras[].label` | `string` or `{ after?, before? }` | How a date in the era reads, around one `{date}` slot; a string is `after` |
5346
- | `eras[].label.after` | `string` | How a date from the era's start reads, with exactly one `{date}` |
5347
- | `eras[].label.before` | `string` | How a date before the era's start reads, with exactly one `{date}` |
5348
- | `formats` | map of names to Calendaria patterns | Named date formats; `std` is the preferred default |
5349
- | `culture` | `Address` | The people this lore belongs to — for a `literature` note, the people whose work it is — naming a lore note with `subType: culture`; not on a `culture` note |
5350
- | `form` | `string` | `literature` only: the kind of work in its people's own terms — `epic`, `saga`, `praise-song`, `elegy`. Free text |
5351
- | `subjects` | `Address[]` | `literature` only: the beings, places, gods and other notes the work concerns, and any event, as `note#id` |
5352
- | `parents` | `Address[]` | `culture` only: the larger cultures this one belongs to; each target is a culture lore note — see [culture parents](#culture-parents-and-inherited-fields) |
5353
- | `language` | `Address` | `literature` and `culture` only: the tongue a work is composed in, or a culture's people speak, naming a skill note with `subType: language`; `package-build npc new` draws a culture's names from that language's [name lists](#name-lists) |
5354
- | `events` | event entries — see [Events](#events) | What happened to or at this subject: dated, attributed events |
5355
- | `appearanceProfile` | `{ frame, eye_color, hair_color, skin_color, complexion, height }` | `culture` only: what its people look like, for `package-build npc new` — see [appearance profiles](#appearance-profiles) |
5356
- | `appearanceProfile.frame` | map of `frame` words to positive weights, required | How often each frame is drawn |
5357
- | `appearanceProfile.eye_color` | map of `eye_color` words to positive weights, required | How often each eye colour is drawn |
5358
- | `appearanceProfile.hair_color` | map of `hair_color` words to positive weights, required | How often each hair colour is drawn |
5359
- | `appearanceProfile.skin_color` | map of `skin_color` words to positive weights, required | How often each skin colour is drawn |
5360
- | `appearanceProfile.complexion` | map of `complexion` words to positive weights, required | How often each complexion is drawn |
5361
- | `appearanceProfile.height` | map of `gender` words to `{ min, max }`, required | Height ranges by gender; `female` and `male` are required |
5362
- | `appearanceProfile.height.<name>.min` | height in metres or feet and inches, required | The shortest height drawn for that gender |
5363
- | `appearanceProfile.height.<name>.max` | height in metres or feet and inches, required | The tallest height drawn for that gender |
5532
+ | `data` property | Values | Description |
5533
+ | ------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
5534
+ | `epoch` | canonical `<year>.<day>` | The canonical day that equals calendar year 1, day 1 |
5535
+ | `months` | `{ name, abbreviation?, days }[]` | Ordered months; their days sum to the world's year |
5536
+ | `months[].name` | `string`, required | The month's name |
5537
+ | `months[].abbreviation` | `string` | Its short form, for a compact date |
5538
+ | `months[].days` | `integer`, required | How many days the month holds |
5539
+ | `weekdays` | `{ name, abbreviation? }[]` | Ordered week days, indexed from zero; weeks run continuously |
5540
+ | `weekdays[].name` | `string`, required | The day's name |
5541
+ | `weekdays[].abbreviation` | `string` | Its short form, for a compact date |
5542
+ | `seasons` | `{ name, abbreviation?, start }[]` | Seasons starting on one-based days of year |
5543
+ | `seasons[].name` | `string`, required | The season's name |
5544
+ | `seasons[].abbreviation` | `string` | Its short form, for a compact date |
5545
+ | `seasons[].start` | `integer`, required | The one-based day of the year it begins; starts increase down the list |
5546
+ | `namedDays` | `{ name, abbreviation?, day }[]` | Names assigned to one-based days of year |
5547
+ | `namedDays[].name` | `string`, required | The day's name |
5548
+ | `namedDays[].abbreviation` | `string` | Its short form, for a compact date |
5549
+ | `namedDays[].day` | `integer`, required | The one-based day of the year it names, distinct within the calendar |
5550
+ | `eras` | `{ shortcode, name, abbreviation?, marker?, proclaimedBy?, start, label? }[]` | Year counts; `start` is `null` or an in-calendar year |
5551
+ | `eras[].shortcode` | `string`, required | The era's own segment of `<calendar>.<era>`, unique within the calendar |
5552
+ | `eras[].name` | `string`, required | The era's name |
5553
+ | `eras[].marker` | `string` | Uppercase letters and digits, beginning with a letter, unique across the corpus; an authored date names the era by it |
5554
+ | `eras[].abbreviation` | `string` | Its short form, for a compact date |
5555
+ | `eras[].proclaimedBy` | `Address` | The body that began the reckoning |
5556
+ | `eras[].start` | `null` or `integer`, required | The calendar year the era begins; `null` for the era before year 1 |
5557
+ | `eras[].label` | `string` or `{ after?, before? }` | How a date in the era reads, around one `{date}` slot; a string is `after` |
5558
+ | `eras[].label.after` | `string` | How a date from the era's start reads, with exactly one `{date}` |
5559
+ | `eras[].label.before` | `string` | How a date before the era's start reads, with exactly one `{date}` |
5560
+ | `formats` | map of names to Calendaria patterns | Named date formats; `std` is the preferred default |
5561
+ | `culture` | `Address` | The people this lore belongs to — for a `literature` note, the people whose work it is — naming a lore note with `subType: culture`; not on a `culture` note |
5562
+ | `form` | `string` | `literature` only: the kind of work in its people's own terms — `epic`, `saga`, `praise-song`, `elegy`. Free text |
5563
+ | `subjects` | `Address[]` | `literature` only: the beings, places, gods and other notes the work concerns, and any event, as `note#id` |
5564
+ | `parents` | `Address[]` | `culture` only: the larger cultures this one belongs to; each target is a culture lore note — see [culture parents](#culture-parents-and-inherited-fields) |
5565
+ | `language` | `Address` | `literature` and `culture` only: the tongue a work is composed in, or a culture's people speak, naming a skill note with `subType: language`; `package-build npc new` draws a culture's names from that language's [name lists](#name-lists), and the words of that language belong to the culture's glossary |
5566
+ | `events` | event entries — see [Events](#events) | What happened to or at this subject: dated, attributed events |
5567
+ | `appearanceProfile` | `{ frame, eye_color, hair_color, skin_color, complexion, height }` | `culture` only: what its people look like, for `package-build npc new` — see [appearance profiles](#appearance-profiles) |
5568
+ | `appearanceProfile.frame` | map of `frame` words to positive weights, required | How often each frame is drawn |
5569
+ | `appearanceProfile.eye_color` | map of `eye_color` words to positive weights, required | How often each eye colour is drawn |
5570
+ | `appearanceProfile.hair_color` | map of `hair_color` words to positive weights, required | How often each hair colour is drawn |
5571
+ | `appearanceProfile.skin_color` | map of `skin_color` words to positive weights, required | How often each skin colour is drawn |
5572
+ | `appearanceProfile.complexion` | map of `complexion` words to positive weights, required | How often each complexion is drawn |
5573
+ | `appearanceProfile.height` | map of `gender` words to `{ min, max }`, required | Height ranges by gender; `female` and `male` are required |
5574
+ | `appearanceProfile.height.<name>.min` | height in metres or feet and inches, required | The shortest height drawn for that gender |
5575
+ | `appearanceProfile.height.<name>.max` | height in metres or feet and inches, required | The tallest height drawn for that gender |
5576
+ | `glossary` | `{ name, name_sort?, common?, definition?, pronunciation?, language? }[]` | `culture` only: the culture's own glossary terms — see [glossary terms](#glossary-terms) |
5577
+ | `glossary[].name` | `string`, required | The headword |
5578
+ | `glossary[].name_sort` | `string` | Its sort key; the name when unstated |
5579
+ | `glossary[].common` | `boolean` | `true` for a word a player is expected to know by heart; `false` when unstated |
5580
+ | `glossary[].definition` | `string` | The sense text; stated, it creates the term or replaces every sense its other sources give |
5581
+ | `glossary[].pronunciation` | `string` | A respelling, as `sol-MEH-reh`: letters, apostrophes and hyphens, at most one syllable in capitals |
5582
+ | `glossary[].language` | `Address` | The language the term is in, naming a skill note with `subType: language`; unstated, the note's language |
5364
5583
 
5365
5584
  `data.events` is available on every `lore` subType, and is described in full under [Events](#events).
5366
5585
 
5586
+ #### Glossary terms
5587
+
5588
+ A culture and an affiliation each state their own glossary terms in
5589
+ `data.glossary`: the terms that belong to the people or the members who use
5590
+ them rather than to a language's grammar — euphemisms, idioms, terms of art,
5591
+ and titles that are not ranks. A culture's glossary draws on three sources: the
5592
+ words of every language that reaches it — its own `data.language`, and the
5593
+ `data.language` of each affiliation whose culture it is — the ranks and offices
5594
+ of those affiliations, and the culture's own `data.glossary`. An affiliation's
5595
+ draws on its own ranks and offices, the words of its language, and its own
5596
+ `data.glossary`. `data.glossary` is the primary source, and it is the only one
5597
+ the note writes:
5598
+
5599
+ ```yaml
5600
+ type: lore
5601
+ subType: culture
5602
+ data:
5603
+ language: skill-sinalelng
5604
+ glossary:
5605
+ - { name: síladòr, common: true }
5606
+ - { name: Knight-Captain, common: true }
5607
+ - {
5608
+ name: blood-field,
5609
+ definition: "ground where a battle tore the boundary with the spirit world, and it did not close",
5610
+ }
5611
+ - { name: meet a man about a horse, definition: "to leave without saying why", common: true }
5612
+ ```
5613
+
5614
+ **An entry with a `definition` creates a term**, or replaces every sense the
5615
+ term's other sources give it. **An entry without one changes no sense**: it marks
5616
+ a term the note already has as common, gives it a sort key or a
5617
+ pronunciation. A word of class `name`, `stem` or `pref` is no term, because
5618
+ those classes stand in no glossary.
5619
+
5620
+ **Every entry resolves to a language**, and the first match wins:
5621
+
5622
+ 1. the entry's own `language`;
5623
+ 2. the note it is written on, where that note is a language — which is how
5624
+ every word in a language note's `data.words` is in that language;
5625
+ 3. on an affiliation, its own `data.language`, then the nearest parent
5626
+ affiliation's, nearest first and parents in the order `data.parents` lists
5627
+ them, then its culture's `data.language`;
5628
+ 4. on a culture, its own `data.language`.
5629
+
5630
+ An affiliation's culture is the one it states or inherits, as
5631
+ [Culture in the index](#culture-in-the-index) describes, and a culture's
5632
+ `data.language` includes one it inherits from its
5633
+ [parent cultures](#culture-parents-and-inherited-fields). An idiom glossed in
5634
+ the setting's common tongue, such as the last entry above, takes the culture's
5635
+ language and is marked `common`.
5636
+
5637
+ ```yaml
5638
+ # Affiliations/Wardens.md — an order with a cant of its own
5639
+ type: affiliation
5640
+ subType: order
5641
+ data:
5642
+ culture: lore-sinaleclt
5643
+ language: skill-tradelng
5644
+ glossary:
5645
+ - { name: zarq, common: true } # a word of the cant
5646
+ - { name: gate-oath, definition: "the oath sworn at the gate" } # in the cant
5647
+ - { name: dòrwen, definition: "the warden's oath-sister", language: skill-sinalelng } # in the culture's tongue
5648
+ ```
5649
+
5650
+ Each finding is located at the entry's key:
5651
+
5652
+ - `data.glossary` on a lore note that is not a culture is an error at the key.
5653
+ On any type but `lore` and `affiliation` it is an unknown key.
5654
+ - Two entries of one name in one glossary are an error at the second. Names are
5655
+ compared after NFC normalisation, with case kept.
5656
+ - An entry that resolves to no language is an error at the entry: state its
5657
+ `language`, or `data.language` on the note.
5658
+ - An entry's `language` that names no language skill is an error at the value.
5659
+ - An entry with no `definition` whose name is no word, rank or office of the
5660
+ note is an error at its `name`: it names nothing.
5661
+ - A pronunciation that is not a respelling is an error at the value.
5662
+ - A `definition` that only repeats the one sense it replaces is a warning at the
5663
+ definition, because the entry changes nothing.
5664
+
5665
+ The `glossary` relation holds one row per entry, with the language it resolves
5666
+ to — see [Content tables](#content-tables).
5667
+
5668
+ #### Dictionaries and glossaries
5669
+
5670
+ Two relations hold what a dictionary and a glossary set, and the query that
5671
+ reads them decides what a page shows. **A dictionary sets words**: the words
5672
+ of language notes, each with a class, an etymology, its senses and its
5673
+ citations. **A glossary sets terms**: the `data.glossary` entries of cultures
5674
+ and affiliations, and the ranks and offices of affiliations, each sense tagged
5675
+ with the body or culture it comes from, and none with a class. Any note may
5676
+ carry either kind of fence, with any `WHERE` clause — see
5677
+ [dictionaries and glossaries](../authoring/links-and-markup.md#dictionaries-and-glossaries)
5678
+ for the layouts and the queries.
5679
+
5680
+ **`dictionary`** is one row per word per language it is listed under. A
5681
+ language lists its own words at `depth` 0 and the words it inherits through
5682
+ `data.family` above that, nearest first, so `WHERE language = '<Address>'` is a
5683
+ language's whole word list, and `WHERE depth = 0` lists every word once, under
5684
+ the language that states it. Where a daughter and a parent state the same
5685
+ form, the daughter's own word is the one listed. Words of class `name`, `stem`
5686
+ and `pref` are no entry.
5687
+
5688
+ | Column | What it holds |
5689
+ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
5690
+ | `language` | The language the word is listed under. |
5691
+ | `language_tag` | That language note's `data.abbreviation`, or its full name where it states none. |
5692
+ | `name` | The word's form. |
5693
+ | `name_sort` | Its sort key: a glossary entry's that names it, else the word's, else the form. |
5694
+ | `letter` | The heading letter: the first letter of `name_sort`, accents taken off and apostrophes skipped, in capitals. |
5695
+ | `pronunciation` | A glossary entry's that names it, else the word's; `NULL` where neither states one. |
5696
+ | `class` | The word's class, as written. |
5697
+ | `etymology` | `{ kind, language, language_tag, form, parts, root, frame }` from the word's `source`, `built`, `root` and `frame`; an inherited word's is `inherited` from the language that states it. `NULL` where there is none. |
5698
+ | `senses` | A list of `{ text, bodies }`: the word's gloss, with no bodies. |
5699
+ | `usage` | The word's `attested` Addresses. |
5700
+ | `common` | Whether a glossary entry that names the word marks it a word a player is expected to know by heart. |
5701
+ | `depth` | How far up the language's lineage the word is stated: `0` for its own. |
5702
+
5703
+ **`terms`** is one row per term per note that owns it: an entry of a culture's
5704
+ or an affiliation's `data.glossary`, or an affiliation's rank or office.
5705
+
5706
+ | Column | What it holds |
5707
+ | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
5708
+ | `owner` | The Address of the culture or affiliation that owns the term. |
5709
+ | `owner_kind` | `culture` or `affiliation`. |
5710
+ | `language` | The language the term is in: a glossary entry's, as it resolves; a rank's or office's, the language its body's glossary resolves to. `NULL` where none resolves. |
5711
+ | `language_tag` | That language note's `data.abbreviation`, or its full name where it states none. |
5712
+ | `name` | The headword. |
5713
+ | `name_sort` | Its sort key: a glossary entry's, else the name. |
5714
+ | `letter` | The heading letter, as for a word. |
5715
+ | `pronunciation` | A glossary entry's; `NULL` where none states one. |
5716
+ | `senses` | A list of `{ text, bodies }`, each sense naming the owner. |
5717
+ | `usage` | The Addresses the term is cited from: a rank's body and every being at that level, an office's body, holders and every being holding it, an entry's owner. |
5718
+ | `common` | Whether a glossary entry marks it a word a player is expected to know by heart. |
5719
+ | `origins` | Which sources gave it: `rank`, `office`, `glossary`. |
5720
+ | `cultures` | Every culture the term reaches, as a list of Addresses. |
5721
+
5722
+ **A term reaches the cultures its owner reaches.** A culture's own terms reach
5723
+ the culture. An affiliation's terms — its ranks, its offices and its own
5724
+ glossary entries alike — reach its culture, its own or one the index resolves
5725
+ for it, and the culture of every affiliation above it through `data.parents`.
5726
+ A culture's glossary is therefore `WHERE list_contains(cultures, '<Address>')`,
5727
+ and an affiliation's is `WHERE owner = '<Address>'`. An affiliation that
5728
+ reaches no culture has an empty list.
5729
+
5730
+ **A rank and an office of one title in one body are one row**, the rank's
5731
+ sense first, and where their descriptions are identical, one sense.
5732
+
5733
+ **A rank or office title of one word** — no space and no hyphen in it, as
5734
+ `Subject` or `Member` — is a term only where a glossary entry of its body, or
5735
+ of a culture it reaches, names it, or where it is a word of its language,
5736
+ compared without case. A title of several words, as `Knight-Captain` or
5737
+ `Lord Steward`, always is.
5738
+
5739
+ **A glossary entry with a `definition`** replaces the senses of its owner's own
5740
+ rank or office of that name. Otherwise it is a term of its owner's own, cited
5741
+ from the owner, so a culture's definition of a rank one of its affiliations
5742
+ holds is a second row beside the rank's, and a glossary of that culture sets
5743
+ both senses under one headword.
5744
+
5745
+ **A glossary entry without a `definition`** changes no sense. It marks what it
5746
+ names: the word or the rank or office of its name in the language it resolves
5747
+ to, among what reaches its note — for a culture, the words of its language and
5748
+ of each affiliation's language, and the ranks and offices of the affiliations
5749
+ that reach it; for an affiliation, its own ranks and offices and the words of
5750
+ its language — and failing that, the one of its name in any language that
5751
+ reaches the note. Names are compared after NFC normalisation, with case kept.
5752
+ Marking sets `common`, and replaces `name_sort` and `pronunciation`, on the
5753
+ word's row in that language or on the rank's or office's row, and lets a
5754
+ one-word title in. Where several entries state one of these, the first in the
5755
+ order of the owner's Address wins.
5756
+
5757
+ **The datafiles.** `package-build content-index` writes each relation beside
5758
+ the index: `<package>-dictionary.jsonl`, sorted by `language`, `name_sort` and
5759
+ `name`, and `<package>-terms.jsonl`, sorted by `owner`, `name_sort` and
5760
+ `name`. Each holds one row per line, keys sorted at every depth, and is
5761
+ byte-identical between two builds of an unchanged tree. They hold every note's
5762
+ rows, GM notes' included, as the index does. Nothing in the build reads them;
5763
+ they are there for `jq`, an editor or another package.
5764
+
5367
5765
  #### Calendar note structure
5368
5766
 
5369
5767
  The calendar keys belong to `subType: calendar`. A calendar declares `months`, `epoch`, and `eras`. The world's `place` note declares the year length; the calendar's months sum to that length. A calendar with no week omits `weekdays`.
@@ -6327,3 +6725,54 @@ under](#the-document-id): a folder and an item sharing a shortcode would
6327
6725
  otherwise derive the same id, and Foundry keys the two in separate collections
6328
6726
  so neither would complain. The content index publishes this value, so a consumer
6329
6727
  reads a folder's id rather than recomputing one.
6728
+
6729
+ ### Word classes
6730
+
6731
+ A word names its word class as a base followed by zero or more
6732
+ modifiers, separated by spaces: `n I f` is a noun of class I, feminine. The
6733
+ first token is always the base, so `n n` is a neuter noun. A hyphen does not
6734
+ separate tokens, and `n-coll` is refused; the collective noun is `n coll`.
6735
+
6736
+ A base is one of these. A base marked `no` never prints in the glossary.
6737
+
6738
+ | Base | Abbreviation | Expansion | In the glossary |
6739
+ | ------ | ------------ | --------------- | --------------- |
6740
+ | `n` | n. | noun | yes |
6741
+ | `v` | v. | verb | yes |
6742
+ | `adj` | adj. | adjective | yes |
6743
+ | `adv` | adv. | adverb | yes |
6744
+ | `pron` | pron. | pronoun | yes |
6745
+ | `num` | num. | numeral | yes |
6746
+ | `part` | part. | particle | yes |
6747
+ | `art` | art. | article | yes |
6748
+ | `conj` | conj. | conjunction | yes |
6749
+ | `dem` | dem. | demonstrative | yes |
6750
+ | `rel` | rel. | relative marker | yes |
6751
+ | `call` | call | call | yes |
6752
+ | `name` | — | proper name | no |
6753
+ | `stem` | — | bound stem | no |
6754
+ | `pref` | — | prefix | no |
6755
+
6756
+ A modifier is one of these. The neuter prints `nt.`, because `n. n.` would show
6757
+ the base and the gender identically. An expansion beginning `of` follows its
6758
+ base without a comma, so `n I f` reads "noun of class I, feminine".
6759
+
6760
+ | Modifier | Group | Abbreviation | Expansion |
6761
+ | -------- | ----------- | ------------ | ------------------------- |
6762
+ | `m` | gender | m. | masculine |
6763
+ | `f` | gender | f. | feminine |
6764
+ | `n` | gender | nt. | neuter |
6765
+ | `an` | animacy | anim. | animate |
6766
+ | `in` | animacy | inan. | inanimate |
6767
+ | `coll` | number | coll. | collective |
6768
+ | `I` | declension | I | of class I |
6769
+ | `II` | declension | II | of class II |
6770
+ | `III` | declension | III | of class III |
6771
+ | `IV` | declension | IV | of class IV |
6772
+ | `V` | declension | V | of class V |
6773
+ | `hal` | declension | hal. | of the hallowed class |
6774
+ | `â` | conjugation | â | of the first conjugation |
6775
+ | `ê` | conjugation | ê | of the second conjugation |
6776
+ | `i` | conjugation | i | of the third conjugation |
6777
+ | `c` | conjugation | c | of the fourth conjugation |
6778
+ | `dep` | voice | dep. | deponent |