@heroiclands/package-build 25.0.0 → 25.1.1

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 (105) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +1 -0
  3. package/bin/content-commands.mjs +30 -96
  4. package/bin/encoding-commands.mjs +146 -0
  5. package/bin/package-build.mjs +35 -24
  6. package/config.mjs +25 -0
  7. package/convert.mjs +182 -0
  8. package/docs/api.md +144 -6
  9. package/docs/authoring/links-and-markup.md +260 -481
  10. package/docs/commands.md +110 -119
  11. package/docs/configuration.md +31 -13
  12. package/docs/content-format.md +11 -4
  13. package/docs/diagnostics.md +43 -16
  14. package/docs/encoding-core.md +193 -0
  15. package/docs/guides/book.md +2 -2
  16. package/docs/guides/module-project.md +23 -1
  17. package/docs/guides/site.md +2 -2
  18. package/docs/guides/writing-in-invented-scripts.md +458 -575
  19. package/docs/reference/format-details.md +292 -404
  20. package/docs/reference/note-types.md +83 -63
  21. package/docs/source-preview.md +8 -0
  22. package/e2e.mjs +1 -1
  23. package/engine/base-compiler.mjs +27 -9
  24. package/engine/content-charset.mjs +13 -1
  25. package/engine/content-format.yaml +6 -2
  26. package/engine/content-index.mjs +6 -2
  27. package/engine/content-links.mjs +1 -0
  28. package/engine/content-scripts.mjs +614 -459
  29. package/engine/content-scroll.mjs +4 -4
  30. package/engine/content-spans.mjs +40 -9
  31. package/engine/content-verse.mjs +1 -1
  32. package/engine/converter-data.mjs +70 -0
  33. package/engine/dictionary-rows.mjs +2 -34
  34. package/engine/encoding-integrity.mjs +94 -0
  35. package/engine/encoding-language.mjs +506 -0
  36. package/engine/encoding-lexicon.mjs +701 -0
  37. package/engine/encoding-notes.mjs +186 -0
  38. package/engine/encoding-standard.mjs +42 -0
  39. package/engine/encoding-template.mjs +370 -0
  40. package/engine/encoding-terms.mjs +178 -0
  41. package/engine/encoding.mjs +952 -0
  42. package/engine/extension-attributes.mjs +2 -0
  43. package/engine/helpers.mjs +1 -1
  44. package/engine/index.mjs +7 -0
  45. package/engine/language-terms.mjs +17 -16
  46. package/engine/{logographic.mjs → language-writings.mjs} +29 -38
  47. package/engine/markup-validation.mjs +1 -1
  48. package/engine/note-vocabulary.mjs +4 -158
  49. package/engine/pdf-render.mjs +31 -3
  50. package/engine/roman-characters.mjs +135 -16
  51. package/engine/roman-run.mjs +149 -0
  52. package/engine/roman-verse.mjs +95 -0
  53. package/engine/run-rows.mjs +29 -31
  54. package/engine/run-words.mjs +2 -2
  55. package/engine/script-emphasis-terms.mjs +3 -0
  56. package/engine/script-emphasis.mjs +3 -10
  57. package/engine/script-font-delivery.mjs +9 -12
  58. package/engine/script-fonts.mjs +2 -1
  59. package/engine/site-build.mjs +14 -5
  60. package/engine/site-preview.mjs +12 -2
  61. package/engine/sql-tables.mjs +36 -31
  62. package/engine/verse-metre.mjs +1 -1
  63. package/engine/word-forms.mjs +1 -1
  64. package/engine/word-inflections.mjs +48 -0
  65. package/engine/word-rows.mjs +1 -1
  66. package/engine/word-terms.mjs +16 -17
  67. package/package.json +11 -5
  68. package/types/config.d.mts +5 -0
  69. package/types/convert.d.mts +44 -0
  70. package/types/e2e.d.mts +1 -1
  71. package/types/engine/base-compiler.d.mts +4 -2
  72. package/types/engine/content-index.d.mts +4 -1
  73. package/types/engine/content-scripts.d.mts +103 -42
  74. package/types/engine/converter-data.d.mts +11 -0
  75. package/types/engine/dictionary-rows.d.mts +1 -25
  76. package/types/engine/encoding-integrity.d.mts +14 -0
  77. package/types/engine/encoding-language.d.mts +36 -0
  78. package/types/engine/encoding-lexicon.d.mts +259 -0
  79. package/types/engine/encoding-notes.d.mts +39 -0
  80. package/types/engine/encoding-standard.d.mts +11 -0
  81. package/types/engine/encoding-template.d.mts +44 -0
  82. package/types/engine/encoding-terms.d.mts +143 -0
  83. package/types/engine/encoding.d.mts +155 -0
  84. package/types/engine/helpers.d.mts +1 -1
  85. package/types/engine/index.d.mts +4 -0
  86. package/types/engine/language-terms.d.mts +1 -1
  87. package/types/engine/{logographic.d.mts → language-writings.d.mts} +6 -6
  88. package/types/engine/markup-validation.d.mts +1 -1
  89. package/types/engine/pdf-render.d.mts +1 -1
  90. package/types/engine/roman-characters.d.mts +2 -2
  91. package/types/engine/roman-run.d.mts +101 -0
  92. package/types/engine/roman-verse.d.mts +23 -0
  93. package/types/engine/run-rows.d.mts +13 -20
  94. package/types/engine/script-emphasis-terms.d.mts +2 -0
  95. package/types/engine/script-emphasis.d.mts +1 -1
  96. package/types/engine/script-font-delivery.d.mts +2 -3
  97. package/types/engine/script-fonts.d.mts +5 -0
  98. package/types/engine/site-preview.d.mts +1 -1
  99. package/types/engine/word-inflections.d.mts +25 -0
  100. package/engine/map-lexicon.mjs +0 -1045
  101. package/engine/text-maps.mjs +0 -654
  102. package/engine/written-in.mjs +0 -204
  103. package/types/engine/map-lexicon.d.mts +0 -330
  104. package/types/engine/text-maps.d.mts +0 -189
  105. package/types/engine/written-in.d.mts +0 -76
@@ -12,9 +12,9 @@ reads as the language, text that is drawn in the language's own letters, and a
12
12
  way to keep both honest. This guide explains how the toolchain does that, from
13
13
  the model underneath to every finding it can report, for human authors and for
14
14
  agents. The reference pages state each rule exactly: [links and
15
- markup](../authoring/links-and-markup.md#marking-language-font-and-mapping),
16
- [format details](../reference/format-details.md#text-marked-with-a-language-a-font-or-a-map),
17
- [commands](../commands.md#package-build-transliterate-map-text),
15
+ markup](../authoring/links-and-markup.md#marking-language-font-and-encoding),
16
+ [format details](../reference/format-details.md#text-marked-with-a-language-font-or-encoding),
17
+ [commands](../commands.md#package-build-encode-text),
18
18
  [configuration](../configuration.md#languages) and
19
19
  [diagnostics](../diagnostics.md). This guide ties them into one account and
20
20
  works one example through from start to finish.
@@ -24,461 +24,189 @@ Semaron script. The lexicon and the notes in them are the guide's own sample.
24
24
 
25
25
  ## The model
26
26
 
27
- These ideas carry everything else.
28
-
29
- 1. **A note records Unicode characters, and nothing else.** Which keyboard
30
- typed them is the author's business. The markdown and the build neither
31
- know nor care.
32
- 2. **A font draws glyphs for code points.** A code point a font lacks draws a
33
- box, so the build refuses it before a box reaches a reader. A font states no
34
- sounds and no spelling, just as a Unicode block chart states none.
35
- 3. **Each script has code points of its own,** as Runic and Ogham have their
36
- own Unicode blocks. Semaron's `a` is not U+0061. Every face of one script
37
- draws the same code points, so changing the face changes only the look. The
38
- left-to-right scripts sit in plane 15, the Supplementary Private Use Area-A
39
- (Semaron from U+F0200). The right-to-left scripts sit in unassigned code
40
- points of plane 1 whose default direction is right to left, so their text
41
- runs right to left in any editor, browser or typesetter with no control
42
- characters.
43
- 4. **Sounds and spelling belong to the language.** A script is not a language:
44
- several languages are written in one script, as many Earth languages share
45
- the Latin alphabet. A mapping therefore belongs to a script and serves every
46
- language written in it. What differs between languages is their dictionaries.
47
- 5. **Language and presentation are separate.** `lang=` identifies the
48
- language, retaining the authored Roman spelling and the surrounding prose
49
- font. Language and vocabulary checks still apply.
50
- 6. **`.nativefont` requests native presentation.** It requires an explicit
51
- `lang=` on that same span, div or fence, then selects the language's
52
- configured native map and font. Each marked child makes its own choice;
53
- native presentation is not inherited by a language-only child.
54
- 7. **`font=` and `map=` override independently.** A native run takes any
55
- missing half from the language's `data.writtenIn`; if neither the run nor
56
- its language provides a font, the build reports the needed `font=` override.
57
- When the language supplies no map, native code points can be typed directly.
58
- A native font or map without `.nativefont` is an error. Ordinary Roman
59
- `font=mono`, `serif` and `sans` remain available without native opt-in.
60
- 8. **Native mapping uses the dictionary first.** A word the dictionary does
61
- not write is spelled by the mapping's rows. Every word must belong to the
62
- language where its configured note supplies a dictionary.
63
-
64
- A script that writes meanings rather than sounds is **logographic**. It has no
65
- sound rules, so every word is written from the dictionary and never guessed;
66
- see [logographic scripts](#logographic-scripts).
67
-
68
- One code path serves the build and its users. `package-build transliterate`
69
- converts a passage exactly as the build does, and `package-build lexicon` lists
70
- every word of a language with its native form; see
71
- [the commands](#the-commands).
72
-
73
- The pipeline for one marked run reads left to right:
74
-
75
- | Stage | Reads | Reports |
76
- | ------------- | ------------------------------------ | ------------------------------------------ |
77
- | Keys | `lang=`, `font=`, `map=` | an unknown language, font or map |
78
- | Dictionary | the language's `data.words[].native` | a native form the rows cannot capitalise |
79
- | Rows | the mapping's `data.map.rows` | nothing: a character with no row passes on |
80
- | Lexicon check | the language's words and register | a word the language does not hold |
81
- | Face coverage | the face's `fonts.json` code points | a character the face does not draw |
82
- | Element | the face, the tag and the direction | nothing |
27
+ A language defines its words, meanings and pronunciation. An encoding defines
28
+ which source sequences write which characters. A face draws those characters.
29
+ Keeping these facts in their owning sources gives each passage one consistent
30
+ conversion on the site, in Foundry and in the book.
31
+
32
+ | Fact | Source | Selection |
33
+ | -------------------------- | ----------------------------------------- | ------------------------------ |
34
+ | Language and vocabulary | Configuration and language note | `lang=` |
35
+ | Authored lexical spelling | Word, affix or grammatical `native` entry | Encoding and optional face |
36
+ | Encoding rows and controls | Encoding document frontmatter | `enc=` document Address |
37
+ | Font face | Installed `fonts.json` | `font=` or the encoding's font |
38
+
39
+ Language identification alone retains Roman text. `enc=` selects an encoding
40
+ document, which infers its language and font. Unknown source characters are
41
+ errors; a face drawing a character does not make it a spelling mapping.
42
+
43
+ An encoding uses exact round trips by default. If its script omits Roman
44
+ distinctions, its note can explicitly select `rules.roundTrip: canonical`.
45
+ Decoding then gives an authored conventional spelling; authoritative lexicon
46
+ entries can recover fuller word forms. The encoder adds no invisible metadata
47
+ to restore erased spelling. See the [canonical encoding contract](../encoding-core.md#executable-rules)
48
+ for aliases, inherent vowels and shared word separators.
83
49
 
84
50
  ## Language and native presentation
85
51
 
86
- The keys go in the attribute block that a span, a fenced div or a `verse`
87
- fence already takes:
88
-
89
- | Written | Meaning |
90
- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
91
- | `[Daimela, helthos.]{lang=vyl}` | Vylari in authored Roman spelling and the page's font, with language validation. |
92
- | `[Zaron lonak]{lang=vyl .nativefont}` | Vylari mapped and drawn with its configured native defaults. |
93
- | `[Zaron lonak]{lang=vyl .nativefont font=semaron-kuriale map=semaron}` | Native Vylari with both defaults explicitly overridden. |
94
- | `[ad astra]{lang=la font=serif}` | A real language's BCP 47 tag, in the reader's serif. |
95
- | `::: {lang=vyl .nativefont}` … `:::` | Native presentation for a passage's unmarked paragraphs. |
96
- | a `verse` fence opened with `{lang=vyl .nativefont}` | Native presentation for a poem's unmarked lines. |
97
-
98
- The class is explicit on each construct. `[Zaron]{.nativefont}` is an error
99
- even inside a passage marked `lang=vyl`: add `lang=vyl` to that span. A native
100
- font such as `font=semaron-kuriale`, or any `map=`, requires `.nativefont`.
101
- Generic Roman `font=serif`, `sans` and `mono` do not.
102
-
103
- **`lang=`** is read in this order. First a short name declared in the
104
- configuration's `languages`; then a BCP 47 language tag for a real language
105
- (`la`, `grc`, `en-GB`), checked for its form alone. A declared name wins over a
106
- tag spelled the same way. A well-formed tag one edit from a declared name draws
107
- a warning that names the declared one, since `vly` is a valid tag for another
108
- language than `vyl`.
109
-
110
- **`font=`** is a face of `@heroiclands/fonts`, named by its class without
111
- `hl-` (`hl-semaron-kuriale` is `font=semaron-kuriale`), or one of the generic
112
- families `mono`, `serif` and `sans`. A package that sets text in a face adds the
113
- fonts to its own development dependencies:
52
+ `enc=` names an encoding document, with `doc` as its default Address type:
53
+ `[say.]{enc=aelwsemaronenc}` selects `doc-aelwsemaronenc`. That document supplies
54
+ its configured `language` and single `font`; an explicit `lang=` must identify
55
+ the same language, and an explicit `font=` must be that font. Another face is
56
+ a separate encoding document.
114
57
 
115
- ```bash
116
- npm install --save-dev @heroiclands/fonts
117
- ```
118
-
119
- The installed package's `fonts.json` lists every face and every code point it
120
- draws, which is the list to write a mapping from. This prints Semaron Kuriale's:
121
-
122
- ```bash
123
- jq -r '.families[] | select(.class == "hl-semaron-kuriale") | .codepoints[] | [.codepoint, .kind, .glyph, .name] | @tsv' \
124
- node_modules/@heroiclands/fonts/fonts.json
125
- ```
126
-
127
- Each line is a code point, its kind (`letter`, `digraph`, `numeral`, `point`, `mark`
128
- or `sign`), the glyph's name and the character's name; the glyph named `a` is the
129
- letter that writes the sound `a`, and `a.long` the long vowel. Those names are
130
- what a mapping's rows are written from.
131
-
132
- On the site `serif` and `mono` are the site's own body and code faces and
133
- `sans` is the reader's sans-serif; in Foundry they are the reader's generic
134
- families; in a book they are the book's own serif, sans and mono. The generic
135
- families are not checked for coverage.
136
-
137
- **`map=`** is the `data.map.name` of a [mapping note](#mapping-notes). It
138
- rewrites the run before anything else; a run's `font=` is then one of the faces
139
- the mapping lists.
140
-
141
- The `languages` block of `package-build.config.yaml` declares the short names:
142
-
143
- ```yaml
144
- languages:
145
- vyl: { tag: art-x-vyl, note: skill-vylarilng }
146
- khel: { tag: art-x-khel, note: skill-khelathlng }
147
- ```
148
-
149
- A name is lowercase letters, digits and hyphens, starting with a letter. `tag`
150
- is a BCP 47 tag; a constructed language takes `art-x-<name>`. `note` is the
151
- address of the language's `skill` note of `subType: language`, whose
152
- `data.words` is the dictionary and whose `data.register` is the names register.
153
- Without `note`, `lang=` only tags: there is no dictionary and no lexicon check.
154
- `package-build lint` checks at the key that `note` names a language note.
155
-
156
- **A language names its script.** Most tongues are written in one script, so the
157
- language note says which, as one map and one face:
158
-
159
- ```yaml
160
- data:
161
- roman: "ABDEFGHIKLMNOPRSTUVWYZabdefghiklmnoprstuvwyzÁÉÍÓÚÝáéíóúýËëÀà0123456789."
162
- writtenIn: { map: semaron, font: semaron-kuriale }
163
- ```
164
-
165
- A language-only run stays Roman: `[Zaron lonak]{lang=vyl}`. Add
166
- `.nativefont` to select both defaults:
58
+ `enc=default` leaves Roman text as written and is the default when no encoding
59
+ is selected. `lang=` still identifies the text and selects its language's
60
+ vocabulary; `font=mono`, `serif`, `sans` or an installed font selects its face
61
+ without encoding it. Language identification does not select an encoding.
167
62
 
168
63
  ```markdown
169
- [Zaron lonak]{lang=vyl .nativefont}
170
- [Zaron lonak]{lang=vyl .nativefont font=semaron-kuriale map=semaron}
64
+ [say.]{enc=aelwsemaronenc}
65
+ [say.]{enc=doc-aelwsemaronenc lang=aelw font=semaron-kuriale}
66
+ [say.]{lang=aelw enc=default}
171
67
  ```
172
68
 
173
- Those two spans display the same native text. `font=` replaces only the font;
174
- `map=` replaces only the map. A missing font default produces an error naming the
175
- needed `font=` override, and incompatible map/font pairs are errors. A map
176
- is optional when the language supplies none; directly typed signs then
177
- reach the selected font unchanged. The build does
178
- not discard a default to make an override fit. This applies to spans, divs,
179
- verse fences, scroll fences and standalone wikilinks. The language note's
180
- keys are checked at their values:
69
+ Class-only emphasis and name children belong to the parent's complete logical
70
+ text; encoding preserves their rendered wrappers. A child with its own run
71
+ keys makes a separate presentation choice. `enc=default` inside an encoded
72
+ parent keeps the inherited language but does not inherit its inferred script
73
+ face. An explicitly authored font remains a font choice.
181
74
 
182
- > `` `data.writtenIn.map` is `semoran`, which no mapping note's `data.map.name` declares; did you mean `semaron`? ``
75
+ A name's braces use the shared name controls. Resolved references encode only
76
+ their visible labels, retaining URLs and presentation cues. These keys apply
77
+ to spans, divs, verse fences and scroll fences. A scroll's selected font must
78
+ belong to a script whose note declares `direction: vertical-rl`.
183
79
 
184
- > `` `data.writtenIn.font` is `teng-chou`, which the map `semaron` is not drawn in; it is drawn in `semaron-kuriale` ``
80
+ ## Roman source and literal characters
185
81
 
186
- **Unmarked text follows its passage; marked children choose their own
187
- presentation.** A language-only child stays Roman, even in the same language
188
- as its native parent:
82
+ Language-only text retains the exact spelling and surrounding prose font:
189
83
 
190
84
  ```markdown
191
- ::: {lang=vyl .nativefont}
192
- Zaron lonak, [Zaron lonak]{lang=vyl}, lokamen zaron.
193
- [Gar-Anlagh]{lang=khel .nativefont font=qalezu-hand map=qalezu}
194
- :::
195
- ```
196
-
197
- The first Vylari phrase and the unmarked text use Semaron; the language-only
198
- span gives the Roman reading. The Khelathi span opts in independently and uses
199
- its own map and font. Qalezu runs right to left inside the left-to-right line
200
- with no `dir=` written. An English gloss, `[an oath]{lang=en font=serif}`,
201
- uses the page's serif. A child's own native defaults and overrides are used;
202
- the parent's explicit map and font are not copied into the child.
203
-
204
- **References make the same choice.** The first name below stays Roman; the
205
- second uses Vylari's native defaults:
206
-
207
- ```markdown
208
- [[lore-marendasdty|Marendas]]{lang=vyl}
209
- [[lore-marendasdty|Marendas]]{lang=vyl .nativefont}
210
- ```
211
-
212
- Only the displayed name is mapped and set in the native face. The destination,
213
- spoiler and `.show` behavior, draft cue and book page references are preserved.
214
- Keep adjacent prose and its spaces in sibling spans; a longer native passage
215
- cannot contain an embedded link.
216
-
217
- **What a run may hold.** A marked span or verse line is plain text: no
218
- emphasis (other than the stars a face's run takes, see [emphasis in a
219
- script](#emphasis-in-a-script)), link, image, footnote, wikilink, code or HTML.
220
- A standalone resolved reference may carry the language and native class, as
221
- above; only its label is set. A marked div holds paragraphs and line breaks only. The text is read as written,
222
- so `[`, `]`, `"`, `<<`, `>>`, `!` and `@ # $ % ^ &` are characters; a bracket
223
- pair is markup only when `(` makes a link, `{` a span or a second `[` a
224
- wikilink, and an unbalanced bracket takes a backslash, `\[`. A code fence is
225
- verbatim: `[…]{…}` inside it stays literal text. Text marked with a real
226
- language's tag alone keeps its Markdown when that language is English; a span
227
- with a non-English effective language cannot contain actual bold or italic
228
- markup, even when the language is real.
229
-
230
- The language, font and map keys are errors on a named block such as a secret block. Put a marked
231
- div inside the block instead.
232
-
233
- ## Roman text and directly typed signs
234
-
235
- Language-only text retains its spelling and the surrounding prose font:
236
-
237
- ```markdown
238
- [Zaron lonak]{lang=vyl}
85
+ [Zaron lonak]{lang=vyl enc=default}
239
86
  [Zaron lonak]{lang=vyl font=serif}
240
87
  ```
241
88
 
242
- The second span explicitly chooses the ordinary serif. Neither selects the
243
- language's native defaults. A configured language's existing vocabulary and
244
- language checks still apply; presentation does not turn those checks off.
245
-
246
- Text already typed in a script's code points also opts in explicitly:
247
-
248
- ```markdown
249
- The lintel is cut with [󰈹󰈰]{lang=und .nativefont font=semaron-kuriale}.
250
- ```
251
-
252
- Here `und` identifies an unspecified language with no configured map. The
253
- font receives the typed code points unchanged. If a map is selected, rows
254
- leave characters with no matching row unchanged. The selected face must draw
255
- every resulting character, except whitespace, or the build
256
- reports the source character. Keyboard layouts and Emacs input methods for
257
- the scripts ship under the fonts package's `keyboards/`; an editor font that
258
- carries the script draws its code points where they are typed. Typing Roman
259
- and mapping it keeps the source readable and diffable.
260
-
261
- ## Mapping notes
262
-
263
- A mapping note is a `doc` note whose `data.map` lists the rewrites. **There is
264
- one mapping per script,** serving every language written in it. The note below
265
- is complete; the rows are the Semaron letters this guide's sample uses.
266
-
267
- **`assets/content/Lore/Semaron_Spelling.md`**
268
-
269
- ```markdown
270
- ---
271
- shortcode: mapsemaron
272
- name: { full: Semaron Spelling }
273
- type: doc
274
- subType: reference
275
- data:
276
- map:
277
- name: semaron
278
- vowels: "aeiou"
279
- rows:
280
- - ["a", "󰈰"] # U+F0230
281
- - ["d", "󰈲"] # U+F0232
282
- - ["e", "󰈳"] # U+F0233
283
- - ["h", "󰈶"] # U+F0236
284
- - ["i", "󰈷"] # U+F0237
285
- - ["k", "󰈹"] # U+F0239
286
- - ["l", "󰈺"] # U+F023A
287
- - ["m", "󰈻"] # U+F023B
288
- - ["n", "󰈼"] # U+F023C
289
- - ["o", "󰈽"] # U+F023D
290
- - ["r", "󰈿"] # U+F023F
291
- - ["s", "󰉀"] # U+F0240
292
- - ["t", "󰉁"] # U+F0241
293
- - ["u", "󰉂"] # U+F0242
294
- - ["z", "󰉆"] # U+F0246
295
- - ["aa", "󰉈"] # U+F0248
296
- - ["th", "󰉐"] # U+F0250
297
- - ["Th", "󰉑"] # U+F0251
298
- - ["L", "󰈊"] # U+F020A
299
- - ["T", "󰈑"] # U+F0211
300
- - ["Z", "󰈖"] # U+F0216
301
- - { from: "y", to: "󰉅", before: vowel } # U+F0245
302
- - ["y", "󰈷"] # U+F0237
303
- - [".", "󰉨"] # U+F0268
304
- fonts: [{ font: semaron-kuriale }]
305
- ---
306
-
307
- Semaron Spelling writes roman spelling in the Semaron script. A passage marked with
308
- `map=semaron` is converted by the rows of this note before it is set, so the note's
309
- source stays in roman letters and the page shows the script. Every language written
310
- in Semaron shares these rows.
311
- ```
312
-
313
- The keys of `data.map`:
314
-
315
- | Key | Meaning |
316
- | ------------- | -------------------------------------------------------------------------------------------- |
317
- | `name` | What `map=` names it by: lowercase letters, digits and `-`, unique in the package. Required. |
318
- | `rows` | The rewrites, in authored order. Required. |
319
- | `fonts` | The faces the mapping is drawn in, each by its slug, at least one. Required. |
320
- | `vowels` | The characters that `before: vowel` reads as vowels. Any other letter is a consonant. |
321
- | `logographic` | `true` for a script that writes meanings; see [logographic scripts](#logographic-scripts). |
322
-
323
- **Rows.** A row is `[from, to]`, or `{ from, to, before, at }` with the two
324
- conditions optional. `from` is one or more characters; `to` is any number of
325
- them, none included. A mapping that drops a language's vowels writes `[a, ""]`.
326
- `rows` is a list, never a keyed map, so `A` and `a` are two rows.
327
-
328
- **How a mapping reads text.**
329
-
330
- - **The longest match wins.** In the note above `th` is one letter, and `t` and
331
- `h` are read only where `th` does not match. Among rows of one length, a row
332
- whose condition holds comes before a row with none, and otherwise the earlier
333
- row wins.
334
- - **A row whose condition fails is skipped.** `before: vowel` holds when the next
335
- character is one of `vowels`; `before: consonant` when it is any other
336
- letter; neither holds at the end of the text. `at: start` holds at the first
337
- character of the run or after white space or punctuation; `at: end` holds at
338
- the last character or before white space or punctuation. In the note above,
339
- `y` is the consonant letter before a vowel and the vowel `i` elsewhere.
340
- - **Capitals and marks need rows of their own.** A row matches the character as
341
- written, so `L` and `l` are two rows, and a script with no capitals gives both
342
- the same code point. A period is a character too and needs its own row.
343
- A language's `data.roman` decides which source characters may appear.
344
- - **A character no row matches passes through unchanged,** and the face-coverage
345
- check then reports it. A mapping for a face that draws nothing on roman code
346
- points lists every letter the spelling uses.
347
-
348
- **Fonts.** `fonts` lists each face the mapping may be used with, as a list of
349
- `{ font, rows }`. A face may carry rows of its own, which a run in that face
350
- tries first (longest match among them) before the shared rows. That is the way
351
- to write a sound differently in a variant face. The generic families `mono`,
352
- `serif` and `sans` are not listed. The lint checks that every listed face draws
353
- every code point that its own rows, the shared rows it can reach and the
354
- lexicons' native forms for it write.
355
-
356
- **Code points above U+FFFF in YAML.** A double-quoted YAML string writes a
357
- code point by its escape, and the left-to-right scripts sit in plane 15, so
358
- they take the eight-digit `\U`: `"\U000F0230"` is U+F0230, and the four-digit
359
- `\u` reaches only code points up to U+FFFF. Both forms mean the same thing.
360
- `package-build format --write` (Prettier) rewrites such an escape into the
361
- character itself, so a mapping note on disk usually holds the raw Private Use
362
- characters, as the note above does. Those read only in one of the fonts
363
- package's faces or its editor fonts, and elsewhere show as boxes. The
364
- convention is a trailing comment, `# U+F0230`, on every row, which keeps the
365
- note readable in any editor and survives formatting. A row that writes several
366
- code points lists them all in its comment.
367
-
368
- **Where the checks sit.** Every key is checked at its own line and column:
369
-
370
- > `` `data.map.name` is `Vyl_Semaron`; a map's name is lowercase letters, digits and `-` ``
371
-
372
- > `` `data.map.rows[4]` has `before: consonant`, and the map states no `vowels` to read it by ``
373
-
374
- > `` `data.map.rows[1]` has an empty `from`; a row matches one or more characters ``
375
-
376
- > `` `data.map.fonts` is empty; a map lists each face it is drawn in ``
377
-
378
- > `` `data.map.rows[5]` writes U+F0239, which the font `semaron-kuriale`, listed in `data.map.fonts`, does not draw ``
379
-
380
- A row with a fault is left out of the map and the rest apply.
381
-
382
- ## How a mapping treats punctuation
383
-
384
- The language note's `data.roman` lists its exact permitted Roman source
385
- characters. Include capitals, accents, digits and punctuation explicitly;
386
- `package-build lint` checks them before mapping. A map cannot admit a character
387
- that the language's inventory excludes. There are no automatic punctuation
388
- substitutions.
389
-
390
- The toolchain is used with one convention for marks: **mood, questions and
391
- quotation are carried by words, not marks,** unless a language's note rules
392
- otherwise. A mapping therefore lists the marks its script has and nothing more.
393
- In the sample, only a period writes the script's pause point:
394
-
395
- ```yaml
396
- - [".", "\U000F0268"] # U+F0268
397
- ```
398
-
399
- (Formatting rewrites the escapes, as above.) A character with no row passes
400
- through unchanged, and the face does not draw it, so a question mark in a
401
- mapped run is an error that names the mark and the mapping:
402
-
403
- > `` `?` (U+003F) is not drawn by the font `semaron-kuriale`, and the map `semaron` has no row for it ``
404
-
405
- The remedy is to write the question or the quotation in words, as the language
406
- does, and leave the mark out of the run. A language whose note permits a mark
407
- includes it in `data.roman`; its script needs a row for that mark as well.
408
- Vylari permits period alone as punctuation. Its lexical catch is written with
409
- a grave-accented vowel, as in `kàralzon`, and that vowel needs its own mapping
410
- row; an apostrophe is not another spelling of it.
89
+ The language note's `data.roman` lists additions to the shared standard:
90
+ letters in their accepted cases, accented letters, catch signs and digits.
91
+ List each character once in NFC spelling. Standard punctuation and whitespace
92
+ are supplied automatically. A language that lists uppercase letters accepts
93
+ them; the build changes no authored case.
94
+
95
+ The terminals are `.` for a statement, `?` for a question, `‼` for an imperative
96
+ and `!` for an exclamation. None is required. A comma pauses within a sentence;
97
+ use it instead of `;` or `:`, and use a period between complete sentences.
98
+ Quotation pairs alternate `«…»`, `‹…›`, then `«…»` at successive depths.
99
+ English double quotes, `<<` and `>>` are findings. A closing quote is not a
100
+ terminal for the enclosing sentence.
101
+
102
+ An em dash interrupts a sentence. An ellipsis at a sentence's start resumes
103
+ one; later it abandons one. An interruption or abandonment takes an immediate
104
+ terminal unless it ends the run. Write `…`, not three periods. Markdown
105
+ escapes are removed before encoding, and diagnostics retain the source position.
106
+
107
+ Every `Zs` space separator and U+0009 through U+000D count as whitespace. A run
108
+ of them is one word separator, which the encoding writes by its own rule.
109
+ Newlines in prose are whitespace. In a verse fence, line endings supply line
110
+ boundaries, blank lines supply stanza boundaries, and `--- {.canto}` on its
111
+ own line supplies a canto boundary. Do not type a verse slash `/` inside the
112
+ fence; an inline quotation of verse may use it. Verse indentation is layout,
113
+ not a punctuation character added to the encoded words.
114
+
115
+ A `.name` child in a language run reaches the encoding with standard `{…}`
116
+ name boundaries. An `.emph` child contributes the same letters with emphasis
117
+ presentation. Both inherit the surrounding native writing when they state no
118
+ run keys.
119
+
120
+ Write an exact native character through codage, such as `⟦U+F0230⟧`, or a name
121
+ that the chosen encoding defines. Literal codes are immutable; ordinary letter
122
+ and punctuation rules do not reinterpret their contents. A setting-script
123
+ character pasted directly into a configured language run is a finding. The
124
+ same restriction applies to a face that can draw it.
125
+
126
+ A `!` touching the `[` of another span is Markdown image syntax. Separate the
127
+ terminal from the following span, or escape the terminal.
128
+
129
+ ## Encodings
130
+
131
+ An encoding is a `doc` note. Its `data.language` is a configured language
132
+ alias, `data.font` is one installed font slug, and `data.mappings` maps Roman
133
+ strings to a font glyph code or a sequence of codes. `data.rules` holds the
134
+ closed executable rules. Optional `data.accepts` states accepted characters
135
+ or ranges and `data.vowels` states contextual vowels. The note body explains
136
+ the spelling to a reader.
137
+
138
+ The font supplies each glyph's `code` beside its `codepoint` in `fonts.json`.
139
+ Codes belong to the selected font; the note never repeats a code table.
140
+ Unknown fields, glyph codes, languages and fonts are positioned findings.
141
+ An encoding's output must decode unambiguously, including across adjacent
142
+ mappings and spaces. The compiler refuses indistinguishable spellings rather
143
+ than choosing a guessed reading. See [the executable rules](../encoding-core.md).
144
+
145
+ Mappings match longest first; contextual before-vowel rules precede their
146
+ unconditional alternatives. A case rule may allow lowercase and initial-capital
147
+ groups while refusing other mixed or uppercase group spellings. Encoding keeps
148
+ authored case and never rewrites a word's Roman spelling.
149
+
150
+ Outside literal markers, a run of Unicode Zs characters or U+0009 through
151
+ U+000D writes one space. Other whitespace needs an explicit mapping. Standard
152
+ Roman punctuation, verse and name controls are inherited from the shared
153
+ standard; an explicit mapping can give a standard source its font glyph.
154
+
155
+ ### Literal codes
156
+
157
+ Inside an encoded run, `⟦…⟧` writes the selected font's glyph codes or exact
158
+ Unicode scalars: `[⟦MAN⟧]{enc=khelzethuenc}` where `MAN` is defined, and
159
+ `[⟦U+00E1⟧]{enc=vylsemaronenc}` for an exact character. A Unicode code takes four to
160
+ six uppercase hexadecimal digits, U+0000 through U+10FFFF excluding
161
+ surrogates. Codes come only from the font; Unicode database
162
+ names are not inferred. Whitespace between codes writes nothing. Nested or
163
+ unmatched markers, unknown codes and standard marks absent from the font
164
+ code table are errors. Without an encoding, a literal marker is a finding.
165
+
166
+ Each outer marker writes its own invisible control. Payload occurrences of
167
+ those controls or the literal escape are quoted with U+E0115. Decoding
168
+ unquotes the payload before reading code rows and uses `U+XXXX` for scalars
169
+ with no code name. Variation selectors U+E0100 through U+E01EF are valid
170
+ encoded characters and carry distinctions the visible font does not draw.
411
171
 
412
172
  ## The dictionary first
413
173
 
414
- The sound rules belong to the script and the exceptions belong to the language.
415
- With `lang=` naming a language that has a `note`, the build reads the
416
- language's `data.words` before the rows.
417
-
418
- **How a run is split into words.** A word is a run of letters, combining marks
419
- and digits, with an apostrophe (`'` or `’`), a hyphen or an en dash joining two
420
- such runs into one word. `k'aralzon` is one word and `zaron-lonak` is one word
421
- for lookup. Everything between words is spelled by the rows.
422
-
423
- **How a word is written.** The word is looked up as written, and then in
424
- lowercase. A word that states a `native` form for the run's mapping (for the
425
- run's face where an entry names one, else the one for no face) is written by it
426
- whole. Every other word, with every derived form of an exception word, is
427
- spelled by the rows. There are no stem-level exceptions: the plural of an
428
- exception word is another word, and the rows spell it.
429
-
430
- **A native form states only an exception.** `native` is a list of
431
- `{ map, font, text }` on a word:
174
+ A language's configured note supplies `data.words`, including the nearest
175
+ complete inherited definition from its `data.family` chain. Grammar and names
176
+ remain the current language's own. Native entries use `{enc, font?, text}`.
432
177
 
433
178
  ```yaml
434
- - form: zaron
435
- class: n
436
- field: Law
437
- gloss: an oath
438
- native:
439
- - { map: semaron, text: "\U000F0246\U000F0248\U000F023F\U000F023D\U000F023C" }
179
+ words:
180
+ - form: zaron
181
+ class: n
182
+ field: Law
183
+ gloss: an oath
184
+ native:
185
+ - { enc: vylsemaronenc, text: "\U000F024F\U000F0230\U000F023F\U000F023C" }
440
186
  ```
441
187
 
442
- `map` is the mapping's `name`, `font` optionally limits the form to one face, and
443
- `text` is the word as the script writes it. A form that the rows already give is
444
- redundant, and the lint says so:
445
-
446
- > `` `data.words[0].native[0]` states what the rows already give: the map `semaron` spells `zaron` so in the font `semaron-kuriale`; a native form is stated only where the rows spell the word differently ``
447
-
448
- so a dictionary holds its exceptions and nothing else. The other checks of a
449
- native form (an unknown map, a face the map is not for, a second form for one
450
- map, a character the face does not draw) report at the entry:
451
-
452
- > `` `data.words[0].native[0].map` is `semron`, which is not a map: a native form names the `data.map.name` of a mapping note; did you mean `semaron`? ``
453
-
454
- > `` `data.words[0].native[2]` states a second native form for the map `semaron`; the first is `data.words[0].native[0]` ``
455
-
456
- > `` `data.words[0].native[0]` holds U+F024F, which the font `semaron-kuriale`, listed in the map `semaron`, does not draw ``
457
-
458
- **Capitals.** A capitalised word that is found only in lowercase takes its
459
- capital from the rows: the rows spell the first sound as written and in
460
- lowercase, and where the native form begins with the lowercase spelling, that
461
- beginning is replaced by the capital. A native form that does not begin the way
462
- the rows begin the word cannot be capitalised, and the capitalised word is an
463
- error at its place in the run:
188
+ Every encoding tries the word's own writing, then complete native parts,
189
+ affixes and explicitly named grammatical endings, then its rows. A face-specific
190
+ entry takes precedence. A native entry repeating what all applicable rows give
191
+ is redundant and is reported at that entry.
464
192
 
465
- > `` `Orlan` is capitalised, and the native form the lexicon of `vyl` states for `orlan` under the map `semaron` does not begin as the rows spell its first letter, so the rows cannot capitalise it; state `Orlan` as a word of its own with its native form ``
193
+ Exact case and exact variants are tried before folded lookup. A lowercase
194
+ native exception can take an initial capital where the rows identify its first
195
+ sound in both cases and its native text begins with the lowercase result.
196
+ That prefix is replaced by the capital result. An ambiguous or uninferable
197
+ capital needs an explicitly authored case-specific entry. Explicit entries
198
+ retain authority; their pronunciation or glyph choices are not inferred.
466
199
 
467
- The fix is the one the message names: a lexicon word `Orlan` with its own
468
- native form, which the lookup as written finds first.
469
-
470
- A character the face does not draw inside a native form reports at the word:
471
-
472
- > `` `zaron` is written U+F024F by the lexicon of `vyl` for the map `semaron`, which the font `semaron-kuriale` does not draw ``
473
-
474
- A native run whose explicit `lang=` is a bare BCP 47 tag, and a declared
475
- language with no `note` are spelled by the rows alone.
200
+ A derived form uses a native grammatical ending only when the complete stem
201
+ and ending writings exist. Otherwise the rows spell the complete form. Missing
202
+ parts never invent signs. A bare encoding without a language uses its rows and
203
+ inline codes alone.
476
204
 
477
205
  ## The checks
478
206
 
479
207
  A run with `lang=`, whose language has a `note`, is checked word by
480
208
  word. This applies to Roman language-only runs and native runs alike;
481
- `.nativefont` selects presentation and does not turn vocabulary checks off. The comparison is case folded and NFC, with `’` read as `'`. A word passes
209
+ Encoding selection does not turn vocabulary checks off. The comparison is case folded and NFC, with `’` read as `'`. A word passes
482
210
  when it is any of these:
483
211
 
484
212
  1. **A lexicon word:** a `data.words[].form` of the language. A form listed in a
@@ -541,25 +269,24 @@ Bare `other`, `kept`, `ancient` and `translation` rows are not checked.
541
269
  noun stated with its own plural and the plural the rule gives both pass, and no
542
270
  check asks that a word takes the right ending.
543
271
 
544
- **Face coverage** follows the mapping. A character left over after the rows
545
- reports once per run, at the start of the matched `from`:
272
+ **Encoding coverage** refuses an unknown character at its source position:
546
273
 
547
- > `` `x` (U+0078) is not drawn by the font `semaron-kuriale`, and the map `semaron` has no row for it ``
274
+ > ``"x" (U+0078) is not in the `semaron` encoding``
548
275
 
549
276
  and a row that writes a code point the face lacks reports where it is applied:
550
277
 
551
- > `` `ch` maps to U+F0299 in the map `semaron`, which the font `semaron-kuriale` does not draw ``
278
+ > `` `ch` encodes to U+F0299 in `semaron`, which the font `semaron-kuriale` does not draw ``
552
279
 
553
280
  **Keys that name nothing.** A key that names nothing is the one finding, and
554
281
  the run is not then checked against a face or a lexicon it cannot reach.
555
282
 
556
283
  > `` `semaron-kurial` is not a font: `font=` names an installed face, or `mono`, `serif` or `sans`; did you mean `semaron-kuriale`? ``
557
284
 
558
- > `` `semron` is not a map: `map=` names the `data.map.name` of a mapping note; did you mean `semaron`? ``
285
+ > `` `semron` is not an encoding document ``
559
286
 
560
287
  > `` `q` is not a declared language (`languages` in package-build.config.yaml) or a BCP 47 tag ``
561
288
 
562
- > `` `thuravark-carved` is not a font of the map `semaron`; it is for `semaron-kuriale` ``
289
+ > `` `thuravark-carved` is not a font of encoding `semaron`; it is for `semaron-kuriale` ``
563
290
 
564
291
  > `` `semaron-kuriale` is not a font: `font=` names `mono`, `serif` or `sans`, or a face of @heroiclands/fonts, which this package has not installed ``
565
292
 
@@ -622,7 +349,7 @@ With the note above, a run accepts:
622
349
 
623
350
  A near miss such as _alathe_ is still an unknown word. To see every form the
624
351
  rules give, each beside the word it comes from, run
625
- `package-build lexicon vyl --map semaron --derived`.
352
+ `package-build lexicon vyl --enc vylsemaronenc --derived`.
626
353
 
627
354
  The keys are checked where they sit:
628
355
 
@@ -636,100 +363,71 @@ The keys are checked where they sit:
636
363
 
637
364
  > `` `data.affixes[1].attachesTo[0]` is `verb`, which is not a word class; did you mean `v`? ``
638
365
 
639
- ## Logographic scripts
640
-
641
- **A script that writes meanings is written from the dictionary, never by
642
- sound.** Its mapping note states `logographic: true`, and every word of a run
643
- under it comes from the language's lexicon. The rows write only the text
644
- between words: spaces, points and anything else with no letter. A word never
645
- falls through to the rows, so a word the lexicon gives no writing is an error
646
- rather than a guess.
647
-
648
- A word is written by the first of these that gives a writing, matched case
649
- folded because the script has no capitals:
650
-
651
- 1. **Its own `native` entry** for the map (the run's face where an entry names
652
- one, else the one for no face), or the entry of a set compound written its
653
- own way.
654
- 2. **Its parts, joined.** The roots of the entry's `built` list in order; each
655
- piece is a lexicon word or an affix of `data.affixes` with a `native` entry of
656
- its own, resolved the same way down. A noun's plural and a verb's present
657
- forms are the stem's writing followed by the ending's, which
658
- `data.grammar.native` states once per ending: `plural`, or one of the persons
659
- `I`, `you`, `heShe`, `we`, `youAll` and `they`.
660
- 3. **Its hyphenated parts,** each by these same steps, so `Gar-halzunet` is
661
- `Gar-`'s writing followed by `halzunet`'s.
662
- 4. **Otherwise an error** at the word.
663
-
664
- Forms outside the parts rule (a suffix that replaces a vowel, an agreeing form,
665
- a participle) have no writing of their own. They are coined as lexicon words
666
- with a writing, or written in signs.
366
+ ## Native spellings and parts
367
+
368
+ Every encoding uses the same order for a language word: its own `native`
369
+ entry for that encoding and face; otherwise the writings of all its parts;
370
+ otherwise the encoding's rows. A face-specific entry precedes an entry for no
371
+ face. Exact lexical forms precede folded lookup, so separately authored
372
+ capitalized spellings keep their own native text. A flat variant is a lexical
373
+ form; it does not assign a grammatical role by itself.
667
374
 
668
375
  ```yaml
669
- affixes:
670
- - form: "-unet"
671
- gloss: "a thing made of"
672
- native:
673
- - { map: zethu, text: "𞯇" }
674
- grammar:
675
- plural:
676
- - { ending: "", becomes: "im" }
677
- native:
678
- - { ending: plural, map: zethu, text: "𞯡" }
679
376
  words:
680
377
  - form: halzi
681
378
  class: n
682
- field: The body
683
379
  gloss: heart
380
+ field: Words
684
381
  native:
685
- - { map: zethu, text: "𞪻" }
382
+ - { enc: zethu, text: "\U0001EABB" }
686
383
  - form: halzunet
687
384
  class: n
688
- field: The body
689
385
  gloss: the account
386
+ field: Words
690
387
  built: [halzi, -unet]
388
+ affixes:
389
+ - form: -unet
390
+ gloss: a thing made of
391
+ native:
392
+ - { enc: zethu, text: "\U0001EBC7" }
691
393
  ```
692
394
 
693
- `[halzi halzunet halziim]{lang=khel font=khelathi-zethu-glyphs map=zethu .nativefont}` is
694
- written U+1EABB, then U+1EABB U+1EBC7, then U+1EABB U+1EBE1, with the spaces the
695
- rows write between them. An affix's `native` and `data.grammar.native` are read
696
- only under a logographic map; naming any other map is an error at the entry.
697
-
698
- **Ambiguity.** One roman form that stands for entries written differently is an
699
- error naming every entry's gloss, and so is one form with a writing for one
700
- sense and none for another. Nothing is guessed. List the words with
701
- `package-build lexicon khel --map zethu`: the third column says where each
702
- writing comes from, `own` for a stored writing, `parts` for one the pieces give,
703
- `none` for a word with no writing, and `ambiguous` for a form a run cannot
704
- write. An ambiguous word's second column is still that entry's own writing, so
705
- pick the entry by its gloss and write those signs directly in the run. Signs
706
- typed in a run hold no letter, so they are no word and pass through as typed.
707
-
708
- **Foreign names.** A registered name is written like any other word, so a name
709
- with no writing is an error. A foreign name is written deliberately: its signs
710
- typed directly, or through a mapping whose rows read a sign-by-sign notation
711
- (sign ids and joiners, as in Manuel de Codage input), which an author writes the
712
- way any other mapping is written. A native run with an explicit bare BCP 47
713
- tag such as `lang=und` has no configured dictionary and uses the rows alone;
714
- it still needs `.nativefont`, an explicit map for conversion and an explicit
715
- font where no language default supplies one.
716
-
717
- **An unknown word gets two errors:** the lexicon check reports it is no word of
718
- the language, and the writing check reports it has no writing.
719
-
720
- > `` `zamlu` has no `zethu` writing in the `khel` lexicon, and its parts do not give one; state one, or write the word directly in signs ``
721
-
722
- > `` `nelgir` stands for 2 words of the `khel` lexicon that the map `zethu` writes differently, “a god” and “god”; write the word directly in signs, choosing by its sense in `package-build lexicon khel --map zethu` ``
723
-
724
- > `` `halzi` has no `zethu` writing: the map `zethu` is logographic, and the run names no `lang=` whose lexicon writes it; mark the run with its language, or write the word directly in signs ``
725
-
726
- > `` `data.grammar.native[3]` writes the ending `they`, and `data.grammar.verbs` states no `persons` ``
395
+ A built part is an exact word or affix, then the same form with joining
396
+ hyphens removed. Parts can themselves be built. A plural or present form can
397
+ use the stem's writing followed by its explicitly named ending from
398
+ `grammar.native`, whose entries use `{ending, enc, font?, text}`. These
399
+ writings work under every encoding. Missing parts do not invent signs, and
400
+ recursive parts terminate without a writing. Entries with genuinely different
401
+ writings remain an ambiguity naming every matching gloss.
402
+
403
+ An encoding accepting no letters, such as `zethu` or `chalish-pilam`, needs a
404
+ lexical writing or literal codes for a word. The refusal names the word,
405
+ language and encoding and points to that word.
406
+
407
+ ### Reading script text
408
+
409
+ Without a language, decoding is exactly the encoding's longest-output match.
410
+ With a language, explicit native and composed writings are tried first,
411
+ longest first; a shared writing uses the first listed sense. Literal marker
412
+ spans decode through their inline codes. Unknown unmarked signs in a script
413
+ accepting no letters become `⟦codes⟧` with a finding.
414
+
415
+ For an unmarked word, the lexicon can recover its unwritten sounds from the
416
+ plain decoded skeleton. A unique skeleton gives its form; a shared skeleton
417
+ gives the first authored form. Each word's citation precedes its variants and derived forms, before the next word's citation. Registered names retain their authored order. Complete names are matched longest first across word separators and receive one pair of braces. A known ordinary word keeps its encoded case without a false unknown-word finding. An unknown skeleton keeps the
418
+ plain decoding and produces a located finding. This is decided per word: a
419
+ passage can mix typed words with encoded words. A word carrying any invisible
420
+ mark keeps exact decoding without skeleton recovery; an explicit authored
421
+ native exception still has precedence. Stress marks are preserved by exact
422
+ rows. Names are represented with braces according to the language's rules.
727
423
 
728
424
  ## Emphasis in a script
729
425
 
730
- Inside a run set in a face, `*…*` and `**…**` are written as in English. No
731
- script marks a stressed word by a bold or a slant, so the build sets emphasis by
732
- the scribal convention the text's language or script states:
426
+ Inside a language run, mark a stressed word as `[word]{.emph}`. The span
427
+ inherits its surrounding language, face and encoding, so its letters follow
428
+ the same writing rules as the rest of the run. In English, `.emph` equals
429
+ `*…*`, and `.strong` equals `**…**`. Non-English spans refuse `.strong` and
430
+ Markdown emphasis. The build sets `.emph` by the language's or script's convention:
733
431
 
734
432
  | Convention | Site and Foundry | Book |
735
433
  | ---------- | -------------------------------------------------- | --------------------- |
@@ -752,12 +450,11 @@ data:
752
450
  script:
753
451
  fonts: [semaron-kuriale]
754
452
  emphasis: rubric
755
- strongEmphasis: overline
756
453
  ```
757
454
 
758
455
  `fonts` lists the faces of the script (a face belongs to one script),
759
- `emphasis` sets both `*…*` and `**…**`, and `strongEmphasis` optionally sets
760
- `**…**` alone. A tongue that states nothing takes its script's convention.
456
+ `emphasis` sets `.emph` and accepted English emphasis. A tongue that states
457
+ nothing takes its script's convention.
761
458
 
762
459
  **Non-English spans do not take Markdown emphasis.** Bold and italic markup in
763
460
  a span with an effective non-English language is an error, whether it has a
@@ -766,13 +463,12 @@ English (`en`) and English BCP 47 tags remain eligible. Escaped literal
766
463
  delimiters are not emphasis. This restriction applies throughout the note.
767
464
 
768
465
  `data.emphasis: words` also makes emphasis an error: it says the language marks
769
- stress with a word, so stars in its runs are refused:
466
+ stress with a word, so `.emph` and stars in its runs are refused:
770
467
 
771
468
  > `` `ban` marks emphasis with a word, not a mark; write the word ``
772
469
 
773
- Underscores and strikethrough are errors in a face. A stressed word is best
774
- enclosed whole: a star inside a word splits it, so a row or lexicon word that
775
- crosses the star is not matched.
470
+ A strikethrough is an error in a face. Mark a whole stressed word: a mark
471
+ inside a word splits the text, so a row or lexicon word crossing it cannot match.
776
472
 
777
473
  **The `--rubric` token.** The theme's `--rubric` is a warm red for its dark
778
474
  ground, and the book prints its own. A rubric carries emphasis by colour alone,
@@ -798,7 +494,7 @@ the contents of a fence byte for byte, which is why a poem is a fence and not
798
494
  paragraphs: indentation, line breaks and spans survive formatting untouched.
799
495
 
800
496
  ````markdown
801
- ```verse {lang=vyl font=semaron-kuriale map=semaron .nativefont}
497
+ ```verse {lang=vyl font=semaron-kuriale enc=vylsemaronenc}
802
498
  Lokazor lonak.
803
499
  lokamen zaron.
804
500
  ```
@@ -822,20 +518,20 @@ fills it, and the last row begins a new stanza:
822
518
 
823
519
  ````markdown
824
520
  ```verse {numstep=1 lang=vyl cols=3}
825
- 1 [Lokazor lonak zaron.]{lang=vyl .nativefont}
521
+ 1 [Lokazor lonak zaron.]{lang=vyl enc=vylsemaronenc}
826
522
  3 [He walks the oath road.]{lang=en}
827
- 3 [Lokazor lonak zaron.]{lang=vyl}
523
+ 3 [Lokazor lonak zaron.]{lang=vyl enc=default}
828
524
 
829
- 1 [Lokamen lonak zaron.]{lang=vyl .nativefont}
525
+ 1 [Lokamen lonak zaron.]{lang=vyl enc=vylsemaronenc}
830
526
  2 [Nothing to say]{lang=en}
831
527
  3 [I walk the oath road.]{lang=en}
832
- 3 [Lokamen lonak zaron.]{lang=vyl}
528
+ 3 [Lokamen lonak zaron.]{lang=vyl enc=default}
833
529
 
834
530
  ***
835
531
 
836
- 1 [alathos lokazor.]{lang=vyl .nativefont}
532
+ 1 [alathos lokazor.]{lang=vyl enc=vylsemaronenc}
837
533
  3 [That one walks.]{lang=en}
838
- 3 [alathos lokazor.]{lang=vyl}
534
+ 3 [alathos lokazor.]{lang=vyl enc=default}
839
535
  ```
840
536
  ````
841
537
 
@@ -941,7 +637,7 @@ paragraphs, and each paragraph starts at the top of a fresh column. `rows=`
941
637
  sets the characters in a column and `cols=` the columns in a panel:
942
638
 
943
639
  ````markdown
944
- ```scroll {rows=25 cols=12 lang=tsu map=tengchou .nativefont}
640
+ ```scroll {rows=25 cols=12 lang=tsu enc=tsutengchouenc}
945
641
  chen teng chou hing
946
642
  tan tsou dou rou
947
643
 
@@ -949,8 +645,8 @@ gen chei chou tsuen
949
645
  ```
950
646
  ````
951
647
 
952
- Where the Tantsou note states `writtenIn: { map: tengchou, font: teng-chou }`,
953
- ` ```scroll {rows=25 cols=12 lang=tsu .nativefont} ` is enough.
648
+ The Tantsou encoding document names its language and the `teng-chou` font;
649
+ its Address selects both. A language-only scroll keeps Roman text.
954
650
 
955
651
  The site, the preview and Foundry set it in columns, in a panel that scrolls
956
652
  sideways from the right. The book draws it as panel images from the face's
@@ -1010,38 +706,35 @@ package's own output: the site copies `fonts.css`, the WOFF2 files and the
1010
706
  licence to `fonts/heroiclands/<version>/` beside the pages and links them from
1011
707
  every page; Foundry stages the same files; the book adds the TrueType files to
1012
708
  the typesetter's font search path. A package that has not installed the fonts, or
1013
- whose notes set no text in a face, ships none of it.
709
+ whose notes select no native face, ships none of it.
1014
710
 
1015
711
  ## The commands
1016
712
 
1017
- `package-build transliterate <map> [--lang <language>] [--font <slug>] [--hex] [text..]`
1018
- maps a passage exactly as the build maps a run marked
1019
- `lang=<language> .nativefont map=<map> font=<slug>`. The text is the words given; `-` or none reads standard input.
1020
- `--font` defaults to the language's `writtenIn` face where the mapping is drawn
1021
- in it, and is otherwise required when the mapping lists more than one face.
1022
- `--hex` prints `U+XXXX` code points separated by spaces.
1023
-
1024
- ```console
1025
- $ package-build transliterate semaron --lang vyl --hex zaron lonak
1026
- U+F0246 U+F0248 U+F023F U+F023D U+F023C U+0020 U+F023A U+F023D U+F023C U+F0230 U+F0239
1027
- $ package-build transliterate semaron --hex zaron
1028
- U+F0246 U+F0230 U+F023F U+F023D U+F023C
713
+ `package-build encode --enc <name|file> [--in|-i <file>] [--out|-o <file>] [text..]`
714
+ encodes one string argument or the UTF-8 file named by `--in`; `--in -` reads
715
+ standard input. `package-build decode` takes the same options and reads code
716
+ points back through the encoding. An encoding is a document Address, with `doc` as the default type.
717
+ These commands apply the bare encoding. The [browser conversion API](../api.md#browser-conversion)
718
+ uses the same implementation with a language's vocabulary and lexical recovery.
719
+
720
+ ```sh
721
+ package-build encode --enc vylsemaronenc "zaron lonak"
722
+ package-build encode --enc khelqalezuenc --in roman.txt --out native.txt
723
+ package-build decode --enc khelqalezuenc --in native.txt --out roman.txt
724
+ package-build encode --enc khelzethuenc "⟦MAN⟧"
1029
725
  ```
1030
726
 
1031
- The first run writes `zaron` by its exception, and the second, with no
1032
- `--lang`, spells it by the rows.
1033
-
1034
- `package-build lexicon <language> [--map <map>] [--font <slug>] [--derived] [--json]`
1035
- lists every word of the language with its native form under the map, or under
1036
- the language's `writtenIn` script when `--map` is left out, tab-separated: the form,
1037
- the native form, `exception` or `rows`, the class and the gloss. `--derived`
727
+ `package-build lexicon <language> [--enc <name>] [--font <slug>] [--derived] [--json]`
728
+ lists every word of the language with its native form under the encoding, or under
729
+ the explicitly selected encoding document, tab-separated: the form,
730
+ the native form, `own`, `parts`, `rows` or `none`, the class and the gloss. `--derived`
1038
731
  adds the forms the language's rules derive, each with the word it comes from,
1039
- `--json` prints an array instead, and under a logographic map the third column
732
+ `--json` prints an array instead, and the third column
1040
733
  is `own`, `parts`, `none` or `ambiguous`.
1041
734
 
1042
735
  ```console
1043
- $ package-build lexicon vyl --map semaron | cut -f1,3-
1044
- zaron exception n an oath
736
+ $ package-build lexicon vyl --enc vylsemaronenc | cut -f1,3-
737
+ zaron own n an oath
1045
738
  lonak rows n a road
1046
739
  lokathen rows v to walk
1047
740
  alathos rows pron he, that one
@@ -1055,8 +748,8 @@ thiruthrimar rows n a sworn witness
1055
748
 
1056
749
  This builds a small package from nothing: a language, a script, a couplet and a
1057
750
  poem. It is the guide's own test: every file below is written into a project and
1058
- linted, and the findings printed are the ones the lint reports. The mapping note
1059
- is the one from [mapping notes](#mapping-notes).
751
+ linted, and the findings printed are the ones the lint reports. The sample selects the
752
+ installed Semaron encoding.
1060
753
 
1061
754
  Install the fonts, then write the configuration. A package that declares
1062
755
  dependencies in its configuration fetches their indexes with
@@ -1120,8 +813,7 @@ name: { full: Vylari Language }
1120
813
  type: skill
1121
814
  subType: language
1122
815
  data:
1123
- roman: "ABDEFGHIKLMNOPRSTUVWYZabdefghiklmnoprstuvwyzÁÉÍÓÚÝáéíóúýËëÀà0123456789."
1124
- writtenIn: { map: semaron, font: semaron-kuriale }
816
+ roman: "ABDEFGHIKLMNOPRSTUVWYZabdefghiklmnoprstuvwyzÁÉÍÓÚÝáéíóúýËëÀà0123456789"
1125
817
  core: true
1126
818
  fields:
1127
819
  - { name: Law, holds: "Oaths, seals and roads." }
@@ -1159,7 +851,7 @@ data:
1159
851
  gloss: an oath
1160
852
  concept: oath-noun
1161
853
  native:
1162
- - { map: semaron, text: "󰉆󰉈󰈿󰈽󰈼" }
854
+ - { enc: vylsemaronenc, text: "󰉆󰉈󰈿󰈽󰈼" }
1163
855
  - { form: lonak, class: n, field: Law, gloss: a road, concept: road-noun }
1164
856
  - { form: lokathen, class: v, field: Law, gloss: to walk, concept: walk-verb }
1165
857
  - { form: alathos, class: pron, field: Law, gloss: "he, that one" }
@@ -1169,6 +861,196 @@ The imperial tongue of the sample setting, written in the Semaron script and
1169
861
  read aloud at its court. The lexicon here is a small sample made for the guide.
1170
862
  ```
1171
863
 
864
+ **`assets/content/Docs/Vylari_Encoding.md`**
865
+
866
+ ```markdown
867
+ ---
868
+ type: doc
869
+ subType: reference
870
+ shortcode: vylsemaronenc
871
+ name:
872
+ full: Vylari in Semaron
873
+ data:
874
+ language: vyl
875
+ font: semaron-kuriale
876
+ accepts:
877
+ - a-z
878
+ - A-Z
879
+ - 0-9
880
+ - "'"
881
+ - "-"
882
+ - "`"
883
+ - Á
884
+ - É
885
+ - Í
886
+ - Ó
887
+ - Ú
888
+ - á
889
+ - é
890
+ - í
891
+ - ó
892
+ - ú
893
+ - ’
894
+ vowels: aeiouAEIOUáéíóúÁÉÍÓÚ
895
+ mappings:
896
+ "0": DIGIT_ZERO
897
+ "1": DIGIT_ONE
898
+ "2": DIGIT_TWO
899
+ "3": DIGIT_THREE
900
+ "4": DIGIT_FOUR
901
+ "5": DIGIT_FIVE
902
+ "6": DIGIT_SIX
903
+ "7": DIGIT_SEVEN
904
+ "8": DIGIT_EIGHT
905
+ "9": DIGIT_NINE
906
+ a: ANKRON_MIN
907
+ A: ANKRON_MAJ
908
+ e: ELZA_MIN
909
+ E: ELZA_MAJ
910
+ i: ILTHERA_MIN
911
+ I: ILTHERA_MAJ
912
+ o: ORNETHE_MIN
913
+ O: ORNETHE_MAJ
914
+ u: ULMERA_MIN
915
+ U: ULMERA_MAJ
916
+ b: BRUTHEKA_MIN
917
+ B: BRUTHEKA_MAJ
918
+ d: DEKAZU_MIN
919
+ D: DEKAZU_MAJ
920
+ f: FUNELA_MIN
921
+ F: FUNELA_MAJ
922
+ g: GULMOS_MIN
923
+ G: GULMOS_MAJ
924
+ h: HARLEN_MIN
925
+ H: HARLEN_MAJ
926
+ j: JA_MIN
927
+ J: JA_MAJ
928
+ k: KALPEZA_MIN
929
+ K: KALPEZA_MAJ
930
+ l: LIZELA_MIN
931
+ L: LIZELA_MAJ
932
+ m: MEZARA_MIN
933
+ M: MEZARA_MAJ
934
+ n: NELAMIS_MIN
935
+ N: NELAMIS_MAJ
936
+ p: POZERA_MIN
937
+ P: POZERA_MAJ
938
+ q: QORETHA_MIN
939
+ Q: QORETHA_MAJ
940
+ r: RONETHOS_MIN
941
+ R: RONETHOS_MAJ
942
+ s: SIRETHA_MIN
943
+ S: SIRETHA_MAJ
944
+ t: TALOTHOS_MIN
945
+ T: TALOTHOS_MAJ
946
+ v: VARTHOS_MIN
947
+ V: VARTHOS_MAJ
948
+ w: WA_MIN
949
+ W: WA_MAJ
950
+ y: YALZEMIS_MIN
951
+ Y: YALZEMIS_MAJ
952
+ z: ZARGON_MIN
953
+ Z: ZARGON_MAJ
954
+ ch: CHEROLA_MIN
955
+ Ch: CHEROLA_MAJ
956
+ sh: SHELMORA_MIN
957
+ Sh: SHELMORA_MAJ
958
+ th: THOLERA_MIN
959
+ Th: THOLERA_MAJ
960
+ dh: DHANETHE_MIN
961
+ Dh: DHANETHE_MAJ
962
+ kh: KHORVENA_MIN
963
+ Kh: KHORVENA_MAJ
964
+ gh: GHORMAS_MIN
965
+ Gh: GHORMAS_MAJ
966
+ zh: ZHAREVA_MIN
967
+ Zh: ZHAREVA_MAJ
968
+ ts: TSALEMA_MIN
969
+ Ts: TSALEMA_MAJ
970
+ ng: ENGARIS_MIN
971
+ Ng: ENGARIS_MAJ
972
+ .: POINT
973
+ á:
974
+ - ANKRON_MIN
975
+ - STRESS
976
+ é:
977
+ - ELZA_MIN
978
+ - STRESS
979
+ í:
980
+ - ILTHERA_MIN
981
+ - STRESS
982
+ ó:
983
+ - ORNETHE_MIN
984
+ - STRESS
985
+ ú:
986
+ - ULMERA_MIN
987
+ - STRESS
988
+ Á:
989
+ - ANKRON_MAJ
990
+ - STRESS
991
+ É:
992
+ - ELZA_MAJ
993
+ - STRESS
994
+ Í:
995
+ - ILTHERA_MAJ
996
+ - STRESS
997
+ Ó:
998
+ - ORNETHE_MAJ
999
+ - STRESS
1000
+ Ú:
1001
+ - ULMERA_MAJ
1002
+ - STRESS
1003
+ rules:
1004
+ normalization: NFC
1005
+ matching: longest-first
1006
+ case:
1007
+ single-letter: preserve
1008
+ consonant-group: lowercase-or-initial-uppercase
1009
+ other-group-cases: forbidden
1010
+ stress:
1011
+ modifier: STRESS
1012
+ position: after-stressed-vowel
1013
+ printing: false
1014
+ effect: stress-only
1015
+ changes-sound: false
1016
+ changes-length: false
1017
+ vowel-group-position: first-vowel
1018
+ vowel-groups:
1019
+ reading: established
1020
+ allow-splitting: false
1021
+ unrecognized-adjacent-vowels: separate
1022
+ groups:
1023
+ - ai
1024
+ - au
1025
+ - ei
1026
+ - oi
1027
+ - ou
1028
+ - ae
1029
+ - ie
1030
+ - uo
1031
+ - aa
1032
+ - ii
1033
+ - uu
1034
+ diaeresis:
1035
+ allowed: false
1036
+ contextual-mappings:
1037
+ - match: ny
1038
+ before: vowel
1039
+ emit: NYELIS_MIN
1040
+ - match: Ny
1041
+ before: vowel
1042
+ emit: NYELIS_MAJ
1043
+ y:
1044
+ kind: consonant
1045
+ sound: y in yes
1046
+ vowel-reading: false
1047
+ grave-accent:
1048
+ allowed: false
1049
+ ---
1050
+
1051
+ These mappings write the sample vocabulary in Semaron Kuriale. They preserve its letter case and use the font's published glyph codes. Contextual groups take their stated before-vowel spelling, and stress is retained after the accented vowel so that the written distinction can be read back exactly.
1052
+ ```
1053
+
1172
1054
  A note with a run. The couplet uses a word the lexicon does not hold:
1173
1055
 
1174
1056
  **`assets/content/Lore/Oath_Road.md`**
@@ -1184,7 +1066,7 @@ The lintel over the door of the oath road is cut with a couplet, and the
1184
1066
  stonecutter set the couplet in the script of the court, so that every traveller
1185
1067
  could read it as the court reads it.
1186
1068
 
1187
- [Lokazor lonak thiruthrimar. lokamen zaron.]{lang=vyl font=semaron-kuriale map=semaron .nativefont}
1069
+ [Lokazor lonak thiruthrimar. lokamen zaron.]{lang=vyl font=semaron-kuriale enc=vylsemaronenc}
1188
1070
  ```
1189
1071
 
1190
1072
  Linting reports the unknown word, at its first character:
@@ -1205,8 +1087,7 @@ name: { full: Vylari Language }
1205
1087
  type: skill
1206
1088
  subType: language
1207
1089
  data:
1208
- roman: "ABDEFGHIKLMNOPRSTUVWYZabdefghiklmnoprstuvwyzÁÉÍÓÚÝáéíóúýËëÀà0123456789."
1209
- writtenIn: { map: semaron, font: semaron-kuriale }
1090
+ roman: "ABDEFGHIKLMNOPRSTUVWYZabdefghiklmnoprstuvwyzÁÉÍÓÚÝáéíóúýËëÀà0123456789"
1210
1091
  core: true
1211
1092
  fields:
1212
1093
  - { name: Law, holds: "Oaths, seals and roads." }
@@ -1244,7 +1125,7 @@ data:
1244
1125
  gloss: an oath
1245
1126
  concept: oath-noun
1246
1127
  native:
1247
- - { map: semaron, text: "󰉆󰉈󰈿󰈽󰈼" }
1128
+ - { enc: vylsemaronenc, text: "󰉆󰉈󰈿󰈽󰈼" }
1248
1129
  - { form: lonak, class: n, field: Law, gloss: a road, concept: road-noun }
1249
1130
  - { form: lokathen, class: v, field: Law, gloss: to walk, concept: walk-verb }
1250
1131
  - { form: alathos, class: pron, field: Law, gloss: "he, that one" }
@@ -1255,14 +1136,16 @@ The imperial tongue of the sample setting, written in the Semaron script and
1255
1136
  read aloud at its court. The lexicon here is a small sample made for the guide.
1256
1137
  ```
1257
1138
 
1258
- The lint is now clean, and the conversion can be read. `package-build transliterate`
1259
- prints the code points the build writes for the couplet:
1139
+ The lint is now clean, and the conversion can be read. `package-build encode`
1140
+ checks a passage through the encoding's rows:
1260
1141
 
1261
- ```console
1262
- $ package-build transliterate semaron --lang vyl --hex "Lokazor lonak. lokamen zaron."
1263
- U+F020A U+F023D U+F0239 U+F0230 U+F0246 U+F023D U+F023F U+0020 U+F023A U+F023D U+F023C U+F0230 U+F0239 U+F0268 U+0020 U+F023A U+F023D U+F0239 U+F0230 U+F023B U+F0233 U+F023C U+0020 U+F0246 U+F0248 U+F023F U+F023D U+F023C U+F0268
1142
+ ```sh
1143
+ package-build encode --enc vylsemaronenc "Lokazor lonak. lokamen zaron."
1264
1144
  ```
1265
1145
 
1146
+ The build also applies the language's recorded native writings when setting
1147
+ its runs.
1148
+
1266
1149
  The site sets the run as
1267
1150
 
1268
1151
  ```html
@@ -1293,20 +1176,20 @@ beside its translation, and the second keeps the metre that the court keeps, so
1293
1176
  that a line with the wrong count is reported to the author who wrote it.
1294
1177
 
1295
1178
  ```verse {numstep=1 lang=vyl cols=3}
1296
- 1 [Lokazor lonak zaron.]{lang=vyl .nativefont}
1179
+ 1 [Lokazor lonak zaron.]{lang=vyl enc=vylsemaronenc}
1297
1180
  3 [He walks the oath road.]{lang=en}
1298
- 3 [Lokazor lonak zaron.]{lang=vyl}
1181
+ 3 [Lokazor lonak zaron.]{lang=vyl enc=default}
1299
1182
 
1300
- 1 [Lokamen lonak zaron.]{lang=vyl .nativefont}
1183
+ 1 [Lokamen lonak zaron.]{lang=vyl enc=vylsemaronenc}
1301
1184
  2 [Nothing to say]{lang=en}
1302
1185
  3 [I walk the oath road.]{lang=en}
1303
- 3 [Lokamen lonak zaron.]{lang=vyl}
1186
+ 3 [Lokamen lonak zaron.]{lang=vyl enc=default}
1304
1187
 
1305
1188
  ***
1306
1189
 
1307
- 1 [alathos lokazor.]{lang=vyl .nativefont}
1190
+ 1 [alathos lokazor.]{lang=vyl enc=vylsemaronenc}
1308
1191
  3 [That one walks.]{lang=en}
1309
- 3 [alathos lokazor.]{lang=vyl}
1192
+ 3 [alathos lokazor.]{lang=vyl enc=default}
1310
1193
  ```
1311
1194
 
1312
1195
  ```verse {lang=vyl scan=trimeter}
@@ -1349,7 +1232,7 @@ The door is marked with the same words as the lintel. Although Semaron's
1349
1232
  convention is rubric, this Vylari span uses no Markdown emphasis: spans with a
1350
1233
  non-English language cannot contain bold or italic markup.
1351
1234
 
1352
- [Lokazor lonak. zaron.]{lang=vyl font=semaron-kuriale map=semaron .nativefont}
1235
+ [Lokazor lonak. zaron.]{lang=vyl font=semaron-kuriale enc=vylsemaronenc}
1353
1236
  ```
1354
1237
 
1355
1238
  Finally, the core report lists what the language still lacks:
@@ -1362,6 +1245,6 @@ Vylari Language (skill-vylarilng): 1 of 4 core concepts have no word
1362
1245
  ```
1363
1246
 
1364
1247
  Everything in this package comes from the guide. A new script follows the same
1365
- steps: a mapping note for its face, `languages` entries for the languages
1248
+ steps: an encoding document for its language and font, `languages` entries for the languages
1366
1249
  written in it, a language note with the dictionary, and runs marked with the
1367
1250
  language key and native presentation class.