Compare commits

...

40 Commits

Author SHA1 Message Date
Greg Shuflin 3882f4e791 gitignore generated pdfs 2024-04-10 22:28:28 -07:00
Greg Shuflin c4915f652d Support new #context feature
Typst 0.11 now has the concept of #context's, which can be used with
counters.
2024-04-09 22:54:27 -07:00
Greg Shuflin 4e46b5efcc Minor README fixes 2023-10-04 01:57:49 -07:00
Greg Shuflin e7b417f0d3 Document with-used-abbreviations
And fix some minor formatting concerns
2023-10-04 01:50:21 -07:00
Greg Shuflin e008aa16f4 Clean up abbreviations 2023-10-04 01:48:11 -07:00
Greg Shuflin 55545eeccc Documentation PDF improvements 2023-10-04 01:35:46 -07:00
Greg Shuflin 80844f5575 Set version to 0.2.0 2023-09-24 01:20:57 -07:00
Greg Shuflin 69ec9263a1 Fix readme 2023-09-21 17:41:52 -07:00
Greg Shuflin 169c6c912d TODO note 2023-09-21 00:42:29 -07:00
Greg Shuflin 3304ab8f3e Add 12th gloss 2023-09-21 00:06:42 -07:00
Greg Shuflin 977810e8cd Add source comment for gloss 2023-09-21 00:03:09 -07:00
Greg Shuflin 9e20916e37 Add example of long gloss 2023-09-21 00:02:09 -07:00
Greg Shuflin e4726bbbbd Abbreviations documentation 2023-09-20 23:44:22 -07:00
Greg Shuflin 04e327e5a1 Quick fix 2023-09-19 03:49:32 -07:00
Greg Shuflin 34ae1b4df6 Better examples 2023-09-19 03:40:56 -07:00
Greg Shuflin 1eead20457 Change from snake_case to kebab-case in most places 2023-09-19 03:23:50 -07:00
Greg Shuflin 1753717dd8 Add changelog 2023-09-19 01:45:13 -07:00
Greg Shuflin 9f48e3f9a7 Start to document abbreviations 2023-09-19 01:41:04 -07:00
Greg Shuflin c313590827 Reorganize abbreviations 2023-09-19 01:22:15 -07:00
Greg Shuflin 33862861a8 better header 2023-09-19 01:09:45 -07:00
Greg Shuflin 08c8f3871d correct line break 2023-09-19 01:07:51 -07:00
Greg Shuflin 42c97f6e80 change gloss_count to gloss-count
And document it better
2023-09-19 01:07:23 -07:00
Greg Shuflin c65665036d Change numbered_gloss to numbered-gloss 2023-09-19 00:59:05 -07:00
Greg Shuflin 42c2651178 Use new function everywhere 2023-09-19 00:22:10 -07:00
Greg Shuflin 656597f120 Move remaining glosses to the neew #codeblock 2023-09-18 22:16:40 -07:00
Greg Shuflin 090bf21e44 Use new codeblock fn to avoid markup duplication 2023-09-18 21:56:26 -07:00
Greg Shuflin 4dd6d27026 Allow morphemes to be none 2023-07-31 02:17:09 -07:00
Greg Shuflin 320f1ad0e5 Redefine fmnt custom abbreviation 2023-07-11 01:41:37 -07:00
Greg Shuflin 6592ebd444 Use snake-case 2023-07-11 01:40:00 -07:00
Greg Shuflin 2b5cad038d Use-order and alphabetical used symbols 2023-07-11 01:33:29 -07:00
Greg Shuflin 6fee9d6e4c More misc work 2023-07-11 01:17:41 -07:00
Greg Shuflin 3c87ec58c8 Hide helper function in closure 2023-07-11 00:42:45 -07:00
Greg Shuflin 4519b8da22 WIP tryign out ways to define custom abbreviations 2023-07-10 03:20:42 -07:00
Greg Shuflin ae47d30fef Handle N- (non-) ? 2023-07-10 03:00:53 -07:00
Greg Shuflin ff20fc0c76 More code simplifications 2023-07-10 02:51:34 -07:00
Greg Shuflin fe6de041a5 Ensure ordering of used abbrevs is correct 2023-07-10 02:49:04 -07:00
Greg Shuflin 26e0bbdab6 Flesh out full linguistics abbreviation chart 2023-07-10 02:44:03 -07:00
Greg Shuflin ffec36460d Simplify how tracking abbreviations works 2023-07-10 02:23:15 -07:00
Greg Shuflin c45e645b1a Concision 2023-07-10 02:19:57 -07:00
Greg Shuflin e837580f68 Playing around with how a used abbreviations table might work 2023-07-05 21:12:52 -07:00
12 changed files with 703 additions and 453 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.pdf

9
CHANGELOG.md Normal file
View File

@ -0,0 +1,9 @@
# Changelog
## 0.2.0 (upcoming)
* renamed `numbered_gloss` to `numbered-gloss`, `gloss_count` to `gloss-count`, in light of the
Typst style preference for kebab-case. Also renamed their arguments to use snake-case as well.
* Documented standard abbreviations
* Removed all default gloss line formatting

View File

@ -4,13 +4,15 @@
creating interlinear morpheme-by-morpheme glosses according to the [Leipzig
glossing rules](https://www.eva.mpg.de/lingua/pdf/Glossing-Rules.pdf).
Run `typst compile leipzig-gloss-examples.typ` in the root of the repository to
# Documentation
Run `typst compile documentation.typ` in the root of the repository to
generate a pdf file with examples and documentation. This command is also
codified in the accompanying [justfile](https://github.com/casey/just) as `just
build-example`.
build-doc`.
The definitions intended for use by end users are the `#gloss` and
`#numbered_gloss` functions.
The definitions intended for use by end users are the `gloss` and
`numbered-gloss` functions, and the `abbreviations` submodule.
# Contributing

View File

@ -0,0 +1,58 @@
#import "abbreviations.typ": *
#let custom-abbreviations = (
"FMNT": "Present/Future stem formant",
)
#let fmnt = emit-abbreviation("FMNT")
// An example function that uses `with-used-abbreviations`
#let print_usage_chart = with-used-abbreviations(final-used-abbreviations => {
show terms: t => {
for t in t.children [
#t.term #h(2cm) #t.description\
]
}
let print-abbrevs(abbrv_list) = {
for abbrv in abbrv_list {
let explanation = if abbrv in standard-abbreviations {
standard-abbreviations.at(abbrv)
} else {
custom-abbreviations.at(abbrv)
}
terms((smallcaps(lower(abbrv)), explanation))
}
}
heading(level: 3)[Abbreviations in order of use]
print-abbrevs(final-used-abbreviations.keys())
heading(level: 3)[Abbreviations in alphabetical order]
let sorted-abbreviations = final-used-abbreviations.keys().sorted()
print-abbrevs(sorted-abbreviations)
})
= Some linguistics paper
== Abbreviations used in this document
#print_usage_chart
== The main body of the paper
The #p1#sg pronoun in Spanish is _yo_. The #p2#sg pronoun in Spanish is _tu_.
The six cases of Latin are:
- Nominative (#nom)
- Genitive (#gen)
- Dative (#dat)
- Accusative (#acc)
- Ablative (#abl)
- Vocative (#voc)
The Present/Future stem formant (#fmnt) in Georgian disappears in perfective screeves.

205
abbreviations.typ Normal file
View File

@ -0,0 +1,205 @@
// See https://www.eva.mpg.de/lingua/resources/glossing-rules.php
#let standard-abbreviations = (
"1": "first person",
"2": "second person",
"3": "third person",
"A": "agent-like argument of canonical transitive verb",
"ABL": "ablative",
"ABS": "absolutive",
"ACC": "accusative",
"ADJ": "adjective",
"ADV": "adverb(ial)",
"AGR": "agreement",
"ALL": "allative",
"ANTIP": "antipassive",
"APPL": "applicative",
"ART": "article",
"AUX": "auxiliary",
"BEN": "benefactive",
"CAUS": "causative",
"CLF": "classifier",
"COM": "comitative",
"COMP": "complementizer",
"COMPL": "completive",
"COND": "conditional",
"COP": "copula",
"CVB": "converb",
"DAT": "dative",
"DECL": "declarative",
"DEF": "definite",
"DEM": "demonstrative",
"DET": "determiner",
"DIST": "distal",
"DISTR": "distributive",
"DU": "dual",
"DUR": "durative",
"ERG": "ergative",
"EXCL": "exclusive",
"F": "feminine",
"FOC": "focus",
"FUT": "future",
"GEN": "genitive",
"IMP": "imperative",
"INCL": "inclusive",
"IND": "indicative",
"INDF": "indefinite",
"INF": "infinitive",
"INS": "instrumental",
"INTR": "intransitive",
"IPFV": "imperfective",
"IRR": "irrealis",
"LOC": "locative",
"M": "masculine",
"N": "neuter",
"N-": [non- (e.g. #smallcaps[nsg] nonsingular, #smallcaps[npst] nonpast)],
"NEG": "negation, negative",
"NMLZ": "nominalizer/nominalization",
"NOM": "nominative",
"OBJ": "object",
"OBL": "oblique",
"P": "patient-like argument of canonical transitive verb",
"PASS": "passive",
"PFV": "perfective",
"PL": "plural",
"POSS": "possessive",
"PRED": "predicative",
"PRF": "perfect",
"PRS": "present",
"PROG": "progressive",
"PROH": "prohibitive",
"PROX": "proximal/proximate",
"PST": "past",
"PTCP": "participle",
"PURP": "purposive",
"Q": "question particle/marker",
"QUOT": "quotative",
"RECP": "reciprocal",
"REFL": "reflexive",
"REL": "relative",
"RES": "resultative",
"S": "single argument of canonical intransitive verb",
"SBJ": "subject",
"SBJV": "subjunctive",
"SG": "singular",
"TOP": "topic",
"TR": "transitive",
"VOC": "vocative",
)
// A dictionary used as a set to mark which abbreviations have been used by a call to
// `emit-abbreviation`. Each key in the dictionary is the string symbol of that abbreviation,
// and the value is always `true`.
#let used-abbreviations = state("leipzig-gloss-used-abbreviations", (:))
// Accepts a callback that accepts the state of the `used-abbreviations`
// dictionary at the end of the document. Also an additional debug parameter
#let with-used-abbreviations(callback) = {
locate(loc => {
let final_used-abbreviations = used-abbreviations.final(loc)
callback(final_used-abbreviations)
})
}
#let render-abbreviation(symbol) = smallcaps(lower(symbol))
// Public function. Given a symbol that is a string, emits
// the lowercase version of that string in smallcaps format, and adds
// its use to the `used-abbreviations` table
#let emit-abbreviation(symbol) = {
let mark_used(symbol) = {
used-abbreviations.update(cur => {
cur.insert(symbol, true)
cur
})
}
mark_used(symbol)
render-abbreviation(symbol)
}
#let p1 = emit-abbreviation("1")
#let p2 = emit-abbreviation("2")
#let p3 = emit-abbreviation("3")
#let A = emit-abbreviation("A")
#let abl = emit-abbreviation("ABL")
#let abs = emit-abbreviation("ABS")
#let acc = emit-abbreviation("ACC")
#let adj = emit-abbreviation("ADJ")
#let adv = emit-abbreviation("ADV")
#let agr = emit-abbreviation("AGR")
#let all = emit-abbreviation("ALL")
#let antip = emit-abbreviation("ANTIP")
#let appl = emit-abbreviation("APPL")
#let art = emit-abbreviation("ART")
#let aux = emit-abbreviation("AUX")
#let ben = emit-abbreviation("BEN")
#let caus = emit-abbreviation("CAUS")
#let clf = emit-abbreviation("CLF")
#let com = emit-abbreviation("COM")
#let comp = emit-abbreviation("COMP")
#let compl = emit-abbreviation("COMPL")
#let cond = emit-abbreviation("COND")
#let cop = emit-abbreviation("COP")
#let cvb = emit-abbreviation("CVB")
#let dat = emit-abbreviation("DAT")
#let decl = emit-abbreviation("DECL")
#let def = emit-abbreviation("DEF")
#let dem = emit-abbreviation("DEM")
#let det = emit-abbreviation("DET")
#let dist = emit-abbreviation("DIST")
#let distr = emit-abbreviation("DISTR")
#let du = emit-abbreviation("DU")
#let dur = emit-abbreviation("DUR")
#let erg = emit-abbreviation("ERG")
#let excl = emit-abbreviation("EXCL")
#let F = emit-abbreviation("F")
#let foc = emit-abbreviation("FOC")
#let fut = emit-abbreviation("FUT")
#let gen = emit-abbreviation("GEN")
#let imp = emit-abbreviation("IMP")
#let incl = emit-abbreviation("INCL")
#let ind = emit-abbreviation("IND")
#let indf = emit-abbreviation("INDF")
#let inf = emit-abbreviation("INF")
#let ins = emit-abbreviation("INS")
#let intr = emit-abbreviation("INTR")
#let ipfv = emit-abbreviation("IPFV")
#let irr = emit-abbreviation("IRR")
#let loc = emit-abbreviation("LOC")
#let M = emit-abbreviation("M")
#let N = emit-abbreviation("N")
#let non = emit-abbreviation("N-")
#let neg = emit-abbreviation("NEG")
#let nmlz = emit-abbreviation("NMLZ")
#let nom = emit-abbreviation("NOM")
#let obj = emit-abbreviation("OBJ")
#let obl = emit-abbreviation("OBL")
#let P = emit-abbreviation("P")
#let pass = emit-abbreviation("PASS")
#let pfv = emit-abbreviation("PFV")
#let pl = emit-abbreviation("PL")
#let poss = emit-abbreviation("POSS")
#let pred = emit-abbreviation("PRED")
#let prf = emit-abbreviation("PRF")
#let prs = emit-abbreviation("PRS")
#let prog = emit-abbreviation("PROG")
#let proh = emit-abbreviation("PROH")
#let prox = emit-abbreviation("PROX")
#let pst = emit-abbreviation("PST")
#let ptcp = emit-abbreviation("PTCP")
#let purp = emit-abbreviation("PURP")
#let Q = emit-abbreviation("Q")
#let quot = emit-abbreviation("QUOT")
#let recp = emit-abbreviation("RECP")
#let refl = emit-abbreviation("REFL")
#let rel = emit-abbreviation("REL")
#let res = emit-abbreviation("RES")
#let S = emit-abbreviation("S")
#let sbj = emit-abbreviation("SBJ")
#let sbjv = emit-abbreviation("SBJV")
#let sg = emit-abbreviation("SG")
#let top = emit-abbreviation("TOP")
#let tr = emit-abbreviation("TR")
#let voc = emit-abbreviation("VOC")

373
documentation.typ Normal file
View File

@ -0,0 +1,373 @@
#set document(title: "typst leipzig-glossing documentation")
#import "leipzig-gloss.typ": abbreviations, gloss, numbered-gloss, gloss-count
#show link: x => underline[*#x*]
#let codeblock-old(contents) = block(fill: luma(230), inset: 8pt, radius: 4pt, breakable: false, contents)
#let codeblock(contents, addl-bindings: (:), unevaled-first-line: none) = {
let full-contents = if unevaled-first-line != none {
unevaled-first-line + "\n" + contents
} else {
contents
}
eval(contents, mode: "markup", scope: (gloss: gloss, numbered-gloss: numbered-gloss) + addl-bindings)
block(fill: luma(230), inset: 8pt, radius: 4pt, breakable: false, raw(full-contents, lang: "typst"))
}
// Abbreviations used in this document
#import abbreviations: poss, prog, sg, pl, sbj, obj, fut, neg, obl, gen, com, ins, all, pst, inf
#import abbreviations: art, dat, du, A, P, prf
#let fmnt = abbreviations.emit-abbreviation("FMNT")
#align(center)[#text(17pt)[Typst `leipzig-glossing` Documentation]]
= Introduction
Interlinear morpheme-by-morpheme glosses are common in linguistic texts to give
information about the meanings of individual words and morphemes in the
language being studied. A set of conventions called the *Leipzig Glossing Rules*
was developed to give linguists a general set of standards and principles for
how to format these glosses. The most recent version of these rules can be
found in PDF form at
#link("https://www.eva.mpg.de/lingua/pdf/Glossing-Rules.pdf")[this link],
provided by the Department of Linguistics at the Max Planck Institute for
Evolutionary Anthropology.
There is a staggering variety of LaTex packages designed to properly align and
format glosses (including `gb4e`, `ling-macros`, `linguex`, `expex`, and
probably even more). These modules vary in the complexity of their syntax and
the amount of control they give to the user of various aspects of formatting.
The `typst-leipzig-glossing` module is designed to provide utilities for
creating aligned Leipzig-style glosses in Typst, while keeping the syntax as
intuitive as possible and allowing users as much control over how their glosses
look as is feasible.
This PDF will show examples of the module's functionality and detail relevant
parameters. For more information or to inform devs of a bug or other issue,
visit the module's Github repository
#link("https://github.com/neunenak/typst-leipzig-glossing")
#show raw: x => highlight(fill: luma(230), extent: 1pt)[#x]
= Basic glossing functionality
As a first example, here is a gloss of a text in Georgian, along with the Typst code used to generate it:
#codeblock(
"#gloss(
header: [from \"Georgian and the Unaccusative Hypothesis\", Alice Harris, 1982],
source: ([ბავშვ-ი], [ატირდა]),
transliteration: ([bavšv-i], [aṭirda]),
morphemes: ([child-#smallcaps[nom]], [3S/cry/#smallcaps[incho]/II]),
translation: [The child burst out crying],
)", unevaled-first-line: "#import \"leipzig-gloss.typ\": gloss")
And an example for English which exhibits some additional styling, and uses imports from another file
for common glossing abbreviations:
#codeblock(
"#gloss(
source: ([I'm], [eat-ing], [your], [head]),
source-style: (item) => text(fill: red)[#item],
morphemes: ([1#sg.#sbj\=to.be], [eat-#prog], [2#sg.#poss], [head]),
morphemes-style: text.with(size: 10pt, fill: blue),
translation: text(weight: \"semibold\")[I'm eating your head!],
translation-style: (item) => [\"#item\"],
)
", addl-bindings: (poss: poss, prog: prog, sg: sg, sbj: sbj))
The `#gloss` function has three pre-defined parameters for glossing levels:
`source`, `transliteration`, and `morphemes`. It also has two parameters
for unaligned text: `header` for text that precedes the gloss, and
`translation` for text that follows the gloss.
The `morphemes` param can be skipped, if you just want to provide a source
text and translation, without a gloss:
#codeblock(
"#gloss(
source: ([Trato de entender, debo comprender, qué es lo que ha hecho conmigo],),
source-style: emph,
translation: [I try to understand, I must comprehend, what she has done with me],
)
")
Note that it is still necessary to wrap the `source` argument in an array of length one.
Here is an example of a lengthy gloss that forces a line break:
// adapted from https://brill.com/fileasset/downloads_static/static_publishingbooks_formatting_glosses_linguistic_examples.pdf
#codeblock(
"#gloss(
source: ([Ich],[arbeite],[ein],[Jahr],[um],[das],[Geld], [zu],[verdienen,],[das], [dein],[Bruder], [an],[einem],[Wochenende],[ausgibt.]),
source-style: text.with(weight: \"bold\"),
morphemes: ([I], [work],[ one], [year],[to],[the],[money],[to],[earn,], [that],[your],[brother],[on],[one], [weekend], [spends.]),
translation: [\"I work one year to earn the money that your brother spends in one weekend\"]
)", addl-bindings: (poss: poss, prog: prog, sg: sg, sbj: sbj))
To add more than three glossing lines, there is an additional parameter
`additional-lines` that can take a list of arbitrarily many more glossing
lines, which will appear below those specified in the aforementioned
parameters:
#codeblock(
"#gloss(
header: [Hunzib (van den Berg 1995:46)],
source: ([ождиг],[хо#super[н]хе],[мукъер]),
transliteration: ([oʒdig],[χõχe],[muqʼer]),
morphemes: ([ož-di-g],[xõxe],[m-uq'e-r]),
additional-lines: (
([boy-#smallcaps[obl]-#smallcaps[ad]], [tree(#smallcaps[g4])], [#smallcaps[g4]-bend-#smallcaps[pret]]),
([at boy], [tree], [bent]),
),
translation: [\"Because of the boy, the tree bent.\"]
)
")
//TODO add a custom numbering system that can handle example 18a-c of Kartvelian Morphosyntax and Number Agreement
== Numbering Glosses
The `gloss` function takes a boolean parameter `numbering` which will add an incrementing
count to each gloss. A function `numbered-gloss` is exported for convenience; this is
defined as simply `#let numbered-gloss = gloss.with(numbering: true)`, and is called with the
same arguments as `gloss`:
#codeblock(
"#gloss(
source: ([გვ-ფრცქვნ-ი],),
transliteration: ([gv-prtskvn-i],),
morphemes: ([1#pl.#obj\-peel-#fmnt],),
translation: \"You peeled us\",
numbering: true,
)
#numbered-gloss(
source: ([მ-ფრცქვნ-ი],),
transliteration: ([m-prtskvn-i],),
morphemes: ([1#sg.#obj\-peel-#fmnt],),
translation: \"You peeled me\",
)
", addl-bindings: (pl: pl, obj: obj, sg: sg, fmnt: fmnt))
The displayed number for numbered glosses is iterated for each numbered gloss
that appears throughout the document. Unnumbered glosses do not increment the
counter for the numbered glosses.
The gloss count is controlled by the Typst counter variable `gloss-count`. This
variable can be imported from the `leipzig-gloss` package and manipulated using the
standard Typst counter functions to control gloss numbering:
#codeblock(
"#gloss-count.update(20)
#numbered-gloss(
header: [from _Standard Basque: A Progressive Grammar_ by Rudolf de Rijk, quoting P. Charriton],
source: ([Bada beti guregan zorion handi baten nahia.],),
translation: [There always is in us a will for a great happiness.],
)", addl-bindings: (gloss-count: gloss-count))
== Styling lines of a gloss
Each of the aforementioned text parameters has a corresponding style parameter,
formed by adding `-style` to its name: `header-style`, `source-style`,
`transliteration-style`, `morphemes-style`, and `translation-style`. These parameters
allow you to specify formatting that should be applied to each entire line of
the gloss. This is particularly useful for the aligned gloss itself, since
otherwise one would have to modify each content item in the list individually.
In addition to these parameters, Typsts usual content formatting can be applied
to or within any given content block in the gloss. Formatting applied in this
way will override any contradictory line-level formatting.
#codeblock(
"#gloss(
header: [This text is about eating your head.],
header-style: text.with(weight: \"bold\", fill: green),
source: (text(fill:black)[I'm], [eat-ing], [your], [head]),
source-style: text.with(style: \"italic\", fill: red),
morphemes: ([1#sg.#sbj\=to.be], text(fill:black)[eat-#prog], [2#sg.#poss], [head]),
morphemes-style: text.with(fill: blue),
translation: text(weight: \"bold\")[I'm eating your head!],
)", addl-bindings: (prog: prog, sbj: sbj, poss: poss, sg: sg))
//TODO add `line_styles` param
= Standard Abbreviations
The Leipzig Glossing Rules define a commonly-used set of short abbreviations
for grammatical terms used in glosses, such as #abbreviations.acc for
"accusative (case)", or #abbreviations.ptcp for "participle" (see "Appendix:
List of Standard Abbreviations in the Leipzig Glossing Rules document)
By convention, these are typeset using #smallcaps[smallcaps]. This package
contains a module value `abbreviations`. Individual abbreviations may be
accessed either with Typst field access notation or by importing them from
`abbreviations`:
#codeblock(
"#import abbreviations: obl, sg, prf
#gloss(
header: [(from _Why Caucasian Languages?_, by Bernard Comrie, in _Endangered Languages of the Caucasus and Beyond_)],
source: ([\[qálɐ-m], [∅-kw-á\]], [ɬ’ə́-r]),
morphemes: ([city-#obl], [3#sg\-go-#prf], [man-#abbreviations.abs]),
translation: \"The man who went to the city.\"
)", addl-bindings: (abbreviations: abbreviations), unevaled-first-line: "#import \"leipzig-gloss.typ\": abbreviations")
The full list of abbreviations is as follows:
== Full list of abbreviations
#{
for (abbreviation, description) in abbreviations.standard-abbreviations {
[#abbreviations.render-abbreviation(abbreviation) - #raw(lower(abbreviation)) - #description ]
linebreak()
}
}
== Custom abbreviations
Custom abbreviations may be defined using the `abbreviations.emit-abbreviation` function:
#codeblock(
"#import abbreviations: obl, sg, prf, fut, emit-abbreviation
#let ts = emit-abbreviation(\"TS\")
#gloss(
header: [(from _Georgian: A Structural Reference Grammar_, by George Hewitt)],
source: ([g-nax-av-en],),
morphemes: ([you#sub[2]-see(#fut)#sub[4]-#ts#sub[7]-they#sub[11]],),
translation: \"they will see you\",
)", addl-bindings: (abbreviations: abbreviations), unevaled-first-line: "#import \"leipzig-gloss.typ\": abbreviations")
== Building used-abbreviations pages
A user of `leipzig-glossing` might wish to generate an introductory page
displaying which abbreviations were actually used in the document. The
`abbreviations.with-used-abbreviations` function may be used for this purpose;
see the `abbreviations-used-example.typ` file in `leipzig-glossing` source for an example.
= Further Example Glosses
These are the first twelve example glosses given in #link("https://www.eva.mpg.de/lingua/pdf/Glossing-Rules.pdf").
along with the Typst markup needed to generate them:
#{
gloss-count.update(0)
}
#codeblock(
"#numbered-gloss(
header: [Indonesian (Sneddon 1996:237)],
source: ([Mereka], [di], [Jakarta], [sekarang.]),
morphemes: ([they], [in], [Jakarta], [now]),
translation: \"They are in Jakarta now\",
)")
#codeblock(
"#numbered-gloss(
header: [Lezgian (Haspelmath 1993:207)],
source: ([Gila], [abur-u-n], [ferma], [hamišaluǧ], [güǧüna], [amuq-da-č.]),
morphemes: ([now], [they-#obl\-#gen], [farm], [forever], [behind], [stay-#fut\-#neg]),
translation: \"Now their farm will not stay behind forever.\",
)", addl-bindings: (fut: fut, neg: neg, obl: obl, gen:gen))
#codeblock(
"#numbered-gloss(
header: [West Greenlandic (Fortescue 1984:127)],
source: ([palasi=lu], [niuirtur=lu]),
morphemes: ([priest=and], [shopkeeper=and]),
translation: \"both the priest and the shopkeeper\",
)")
#codeblock(
"#numbered-gloss(
header: [Hakha Lai],
source: ([a-nii -láay],),
morphemes: ([3#sg\-laugh-#fut],),
translation: [s/he will laugh],
)", addl-bindings: (sg: sg, fut: fut))
#codeblock(
"#numbered-gloss(
header: [Russian],
source: ([My], [s], [Marko], [poexa-l-i], [avtobus-om], [v], [Peredelkino]),
morphemes: ([1#pl], [#com], [Marko], [go-#pst\-#pl], [bus-#ins], [#all], [Peredelkino]),
additional-lines: (([we], [with], [Marko], [go-#pst\-#pl], [bus-by], [to], [Peredelkino]),),
translation: \"Marko and I went to Perdelkino by bus\",
)", addl-bindings: (com: com, pl: pl, ins: ins, all: all, pst:pst))
#codeblock(
"#numbered-gloss(
header: [Turkish],
source: ([çık-mak],),
morphemes: ([come.out-#inf],),
translation: \"to come out\",
)", addl-bindings: (inf: inf))
#codeblock(
"#numbered-gloss(
header: [Latin],
source: ([insul-arum],),
morphemes: ([island-#gen\-#pl],),
translation: \"of the islands\",
)", addl-bindings: (gen:gen, pl: pl))
#codeblock(
"#numbered-gloss(
header: [French],
source: ([aux], [chevaux]),
morphemes: ([to-#art\-#pl],[horse.#pl]),
translation: \"to the horses\",
)",addl-bindings: (art:art, pl:pl))
#codeblock(
"#numbered-gloss(
header: [German],
source: ([unser-n], [Väter-n]),
morphemes: ([our-#dat\-#pl],[father.#pl\-#dat.#pl]),
translation: \"to our fathers\",
)", addl-bindings: (dat:dat, pl:pl))
#codeblock(
"#numbered-gloss(
header: [Hittite (Lehmann 1982:211)],
source: ([n=an], [apedani], [mehuni],[essandu.]),
morphemes: ([#smallcaps[conn]=him], [that.#dat.#sg], [time.#dat.#sg], [eat.they.shall]),
translation: \"They shall celebrate him on that date\",
)", addl-bindings: (pl:pl, sg:sg, dat:dat))
#codeblock(
"#numbered-gloss(
header: [Jaminjung (Schultze-Berndt 2000:92)],
source: ([nanggayan], [guny-bi-yarluga?]),
morphemes: ([who], [2#du.#A.3#sg.#P\-#fut\-poke]),
translation: \"Who do you two want to spear?\",
)", addl-bindings: (du:du, sg:sg, fut:fut, A:A, P:P))
#codeblock("
#numbered-gloss(
header: [Turkish (cf. 6)],
source: ([çık-mak],),
morphemes: ([come_out-#inf],),
translation: ['to come out'],
)", addl-bindings: (inf: inf))

View File

@ -2,5 +2,10 @@ default:
just --list
build-example:
typst compile leipzig-gloss-examples.typ
# Build the leipzig-glossing documentation PDF
build-doc:
typst compile documentation.typ
# Build the abbbreviations-used example
build-abbreviations-example:
typst compile abbreviations-used-example.typ

Binary file not shown.

View File

@ -1,282 +0,0 @@
#import "leipzig-gloss.typ": gloss, numbered_gloss, gloss_count
#import "linguistic-abbreviations.typ": *
#show link: x => underline[*#x*]
//#show raw: x => text(fill: rgb("#43464b"))[#x]
#let codeblock(contents) = block(fill: luma(230), inset: 8pt, radius: 4pt, breakable: false, contents)
= Introduction
Interlinear morpheme-by-morpheme glosses are common in linguistic texts to give
information about the meanings of individual words and morphemes in the
language being studied. A set of conventions called the *Leipzig Glossing Rules*
was developed to give linguists a general set of standards and principles for
how to format these glosses. The most recent version of these rules can be
found in PDF form at
#link("https://www.eva.mpg.de/lingua/pdf/Glossing-Rules.pdf")[this link],
provided by the Department of Linguistics at the Max Planck Institute for
Evolutionary Anthropology.
There is a staggering variety of LaTex packages designed to properly align and
format glosses (including `gb4e`, `ling-macros`, `linguex`, `expex`, and
probably even more). These modules vary in the complexity of their syntax and
the amount of control they give to the user of various aspects of formatting.
The `typst-leipzig-glossing` module is designed to provide utilities for
creating aligned Leipzig-style glosses in Typst, while keeping the syntax as
intuitive as possible and allowing users as much control over how their glosses
look as is feasible.
This PDF will show examples of the module's functionality and detail relevant
parameters. For more information or to inform devs of a bug or other issue,
visit the module's Github repository
#link("https://github.com/neunenak/typst-leipzig-glossing")[neunenak/typst-leipzig-glossing].
= Basic glossing functionality
As a first example, here is a gloss of a text in Georgian, along with the Typst code used to generate it:
#gloss(
header_text: [from "Georgian and the Unaccusative Hypothesis", Alice Harris, 1982],
source_text: ([ბავშვ-ი], [ატირდა]),
transliteration: ([bavšv-i], [aṭirda]),
morphemes: ([child-#smallcaps[nom]], [3S/cry/#smallcaps[incho]/II]),
translation: [The child burst out crying],
)
#codeblock[
```typst
#gloss(
header_text: [from "Georgian and the Unaccusative Hypothesis", Alice Harris, 1982],
source_text: ([ბავშვ-ი], [ატირდა]),
transliteration: ([bavšv-i], [aṭirda]),
morphemes: ([child-#smallcaps[nom]], [3S/cry/#smallcaps[incho]/II]),
translation: [The child burst out crying],
)
```
]
And an example for English which exhibits some additional styling, and uses imports from another file
for common glossing abbreviations:
#gloss(
source_text: ([I'm], [eat-ing], [your], [head]),
source_text_style: (item) => text(fill: red)[#item],
morphemes: ([1#sg.#sbj\=to.be], [eat-#prog], [2#sg.#pos], [head]),
morphemes_style: text.with(fill: blue),
translation: text(weight: "semibold")[I'm eating your head!],
)
#codeblock[
```typst
#import "linguistic-abbreviations.typ": *
#gloss(
source_text: ([I'm], [eat-ing], [your], [head]),
source_text_style: (item) => text(fill: red)[#item],
morphemes: ([1#sg.#subj\=to.be], [eat-#prog], [2#sg.#pos], [head]),
morphemes_style: text.with(fill: blue),
translation: text(weight: "semibold")[I'm eating your head!],
)
```
]
The `#gloss` function has three pre-defined parameters for glossing levels:
`source_text`, `transliteration`, and `morphemes`. It also has two parameters
for unaligned text: `header_text` for text that precedes the gloss, and
`translation` for text that follows the gloss.
If one wishes to add more than three glossing lines, there is an additional
parameter `additional_gloss_lines` that can take a list of arbitrarily many more glossing
lines, which will appear below those specified in the aforementioned
parameters:
#gloss(
header_text: [Hunzib (van den Berg 1995:46)],
source_text: ([ождиг],[хо#super[н]хе],[мукъер]),
transliteration: ([oʒdig],[χõχe],[muqʼer]),
morphemes: ([ož-di-g],[xõxe],[m-uq'e-r]),
additional_gloss_lines: (
([boy-#smallcaps[obl]-#smallcaps[ad]], [tree(#smallcaps[g4])], [#smallcaps[g4]-bend-#smallcaps[pret]]),
([at boy], [tree], [bent]),
),
translation: ["Because of the boy, the tree bent."]
)
#codeblock[
```typst
#gloss(
header_text: [Hunzib (van den Berg 1995:46)],
source_text: ([ождиг],[хо#super[н]хе],[мукъер]),
transliteration: ([oʒdig],[χõχe],[muqʼer]),
morphemes: ([ož-di-g],[xõxe],[m-uq'e-r]),
additional_gloss_lines: (
([boy-#smallcaps[obl]-#smallcaps[ad]], [tree(#smallcaps[g4])], [#smallcaps[g4]-bend-#smallcaps[pret]]),
([at boy], [tree], [bent]),
),
translation: ["Because of the boy, the tree bent."]
)
```
]
To number gloss examples, use `#numbered_gloss` in place of `gloss`. All other parameters remain the same.
#numbered_gloss(
source_text: ([გვ-ფრცქვნ-ი],),
source_text_style: none,
transliteration: ([gv-prtskvn-i],),
morphemes: ([1#pl.#obj\-peel-#fmnt],),
translation: "You peeled us",
)
#codeblock[
```typst
#import "linguistic-abbreviations.typ": *
#gloss(
source_text: ([გვ-ფრცქვნ-ი],),
source_text_style: none,
transliteration: ([gv-prtskvn-i],),
morphemes: ([1#pl.#obj\-peel-#fmnt],),
translation: "You peeled us",
```)]
The displayed number for numbered glosses is iterated for each numbered gloss
that appears throughout the document. Unnumbered glosses do not increment the
counter for the numbered glosses.
The gloss count is controlled by the Typst counter variable `gloss_count`. This
variable can be imported from the `leipzig-gloss` package and reset using the
standard Typst counter functions to control gloss numbering.
//TODO add examples here
== Styling lines of a gloss
Each of the aforementioned text parameters has a corresponding style parameter,
formed by adding `_style` to its name: `header_text_style`, `source_text_style`,
`transliteration_style`, `morphemes_style`, and `translation_style`. These parameters
allow you to specify formatting that should be applied to each entire line of
the gloss. This is particularly useful for the aligned gloss itself, since
otherwise one would have to modify each content item in the list individually.
In addition to these parameters, Typsts usual content formatting can be applied
to or within any given content block in the gloss. Formatting applied in this
way will override any contradictory line-level formatting.
#gloss(
header_text: [This text is about eating your head.],
header_text_style: text.with(weight: "bold", fill: green),
source_text: (text(fill:black)[I'm], [eat-ing], [your], [head]),
source_text_style: text.with(style: "italic", fill: red),
morphemes: ([1#sg.#sbj\=to.be], text(fill:black)[eat-#prog], [2#sg.#pos], [head]),
morphemes_style: text.with(fill: blue),
translation: text(weight: "bold")[I'm eating your head!],
)
#codeblock[
```typst
#gloss(
header_text: [This text is about eating your head.],
header_text_style: text.with(weight: "bold", fill: green),
source_text: (text(fill:black)[I'm], [eat-ing], [your], [head]),
source_text_style: text.with(style: "italic", fill: red),
morphemes: ([1#sg.#sbj\=to.be], text(fill:black)[eat-#prog], [2#sg.#pos], [head]),
morphemes_style: text.with(fill: blue),
translation: text(weight: "bold")[I'm eating your head!],
)
```
]
//TODO add `line_styles` param
== Further Example Glosses
These example glosses replicate the ones given in
#link("https://www.eva.mpg.de/lingua/pdf/Glossing-Rules.pdf").
#{
gloss_count.update(0)
}
#numbered_gloss(
header_text: [Indonesian (Sneddon 1996:237)],
source_text: ([Mereka], [di], [Jakarta], [sekarang.]),
morphemes: ([they], [in], [Jakarta], [now]),
translation: "They are in Jakarta now",
)
#numbered_gloss(
header_text: [Lezgian (Haspelmath 1993:207)],
source_text: ([Gila], [abur-u-n], [ferma], [hamišaluǧ], [güǧüna], [amuq-da-č.]),
morphemes: ([now], [they-#obl\-#gen], [farm], [forever], [behind], [stay-#fut\-#neg]),
translation: "Now their farm will not stay behind forever.",
)
#numbered_gloss(
header_text: [West Greenlandic (Fortescue 1984:127)],
source_text: ([palasi=lu], [niuirtur=lu]),
morphemes: ([priest=and], [shopkeeper=and]),
translation: "both the priest and the shopkeeper",
)
#numbered_gloss(
header_text: [Hakha Lai],
source_text: ([a-nii -láay],),
morphemes: ([3#sg\-laugh-#fut],),
translation: [s/he will laugh],
)
#numbered_gloss(
header_text: [Russian],
source_text: ([My], [s], [Marko], [poexa-l-i], [avtobus-om], [v], [Peredelkino]),
morphemes: ([1#pl], [#com], [Marko], [go-#pst\-#pl], [bus-#ins], [#all], [Peredelkino]),
additional_gloss_lines: (([we], [with], [Marko], [go-#pst\-#pl], [bus-by], [to], [Peredelkino]),),
translation: "Marko and I went to Perdelkino by bus",
)
#numbered_gloss(
header_text: [Turkish],
source_text: ([çık-mak],),
morphemes: ([come.out-#inf],),
translation: "to come out",
)
#numbered_gloss(
header_text: [Latin],
source_text: ([insul-arum],),
morphemes: ([island-#gen\-#pl],),
translation: "of the islands",
)
#numbered_gloss(
header_text: [French],
source_text: ([aux], [chevaux]),
morphemes: ([to-#art\-#pl],[horse.#pl]),
translation: "to the horses",
)
#numbered_gloss(
header_text: [German],
source_text: ([unser-n], [Väter-n]),
morphemes: ([our-#dat\-#pl],[father.#pl\-#dat.#pl]),
translation: "to our fathers",
)
#numbered_gloss(
header_text: [Hittite (Lehmann 1982:211)],
source_text: ([n=an], [apedani], [mehuni],[essandu.]),
morphemes: ([#smallcaps[conn]=him], [that.#dat.#sg], [time.#dat.#sg], [eat.they.shall]),
translation: "They shall celebrate him on that date",
)
#numbered_gloss(
header_text: [Jaminjung (Schultze-Berndt 2000:92)],
source_text: ([nanggayan], [guny-bi-yarluga?]),
morphemes: ([who], [2#du.#A.3#sg.#P\-#fut\-poke]),
translation: "Who do you two want to spear?",
)

View File

@ -1,6 +1,8 @@
#let gloss_count = counter("gloss_count")
#import "abbreviations.typ"
#let build_gloss(spacing_between_items, formatters, gloss_line_lists) = {
#let gloss-count = counter("gloss_count")
#let build_gloss(item-spacing, formatters, gloss_line_lists) = {
assert(gloss_line_lists.len() > 0, message: "Gloss line lists cannot be empty")
let len = gloss_line_lists.at(0).len()
@ -28,86 +30,91 @@
args.push(formatter_fn(item))
}
make_item_box(..args)
h(spacing_between_items)
h(item-spacing)
}
}
#let gloss(
header_text: none,
header_text_style: none,
source_text: (),
source_text_style: emph,
header: none,
header-style: none,
source: (),
source-style: none,
transliteration: none,
transliteration_style: none,
morphemes: (),
morphemes_style: none,
additional_gloss_lines: (), //List of list of content
transliteration-style: none,
morphemes: none,
morphemes-style: none,
additional-lines: (), //List of list of content
translation: none,
translation_style: none,
spacing_between_items: 1em,
gloss_padding: 2.0em, //TODO document these
translation-style: none,
item-spacing: 1em,
gloss-padding: 2.0em, //TODO document these
left_padding: 0.5em,
numbering: false,
breakable: false,
) = {
assert(type(source_text) == "array", message: "source_text needs to be an array; perhaps you forgot to type `(` and `)`, or a trailing comma?")
assert(type(morphemes) == "array", message: "morphemes needs to be an array; perhaps you forgot to type `(` and `)`, or a trailing comma?")
assert(type(source) == "array", message: "source needs to be an array; perhaps you forgot to type `(` and `)`, or a trailing comma?")
assert(source_text.len() == morphemes.len(), message: "source_text and morphemes have different lengths")
if morphemes != none {
assert(type(morphemes) == "array", message: "morphemes needs to be an array; perhaps you forgot to type `(` and `)`, or a trailing comma?")
assert(source.len() == morphemes.len(), message: "source and morphemes have different lengths")
}
if transliteration != none {
assert(transliteration.len() == source_text.len(), message: "source_text and transliteration have different lengths")
assert(transliteration.len() == source.len(), message: "source and transliteration have different lengths")
}
let gloss_items = {
if header_text != none {
if header_text_style != none {
header_text_style(header_text)
if header != none {
if header-style != none {
header-style(header)
} else {
header_text
header
}
linebreak()
}
let formatters = (source_text_style,)
let gloss_line_lists = (source_text,)
let formatters = (source-style,)
let gloss_line_lists = (source,)
if transliteration != none {
formatters.push(transliteration_style)
formatters.push(transliteration-style)
gloss_line_lists.push(transliteration)
}
formatters.push(morphemes_style)
gloss_line_lists.push(morphemes)
if morphemes != none {
formatters.push(morphemes-style)
gloss_line_lists.push(morphemes)
}
for additional in additional_gloss_lines {
for additional in additional-lines {
formatters.push(none) //TODO fix this
gloss_line_lists.push(additional)
}
build_gloss(spacing_between_items, formatters, gloss_line_lists)
build_gloss(item-spacing, formatters, gloss_line_lists)
if translation != none {
linebreak()
if translation_style == none {
["#translation"]
if translation-style == none {
translation
} else {
translation_style(translation)
translation-style(translation)
}
}
}
if numbering {
gloss_count.step()
gloss-count.step()
}
let gloss_number = if numbering {
[(#gloss_count.display())]
[(#context gloss-count.display())]
} else {
none
}
@ -118,7 +125,7 @@
dir:ltr, //TODO this needs to be more flexible
left_padding,
[#gloss_number],
gloss_padding - left_padding - measure([#gloss_number],styles).width,
gloss-padding - left_padding - measure([#gloss_number],styles).width,
[#gloss_items]
)
]
@ -126,4 +133,4 @@
)
}
#let numbered_gloss = gloss.with(numbering: true)
#let numbered-gloss = gloss.with(numbering: true)

View File

@ -1,128 +0,0 @@
#let fmnt = smallcaps([fmnt])
/*
Appendix: List of Standard Abbreviations
1 first person
2 second person
3 third person
A agent-like argument of canonical transitive verb
ABL ablative
ABS absolutive
ACC accusative
ADJ adjective
ADV adverb(ial)
AGR agreement
ALL allative
ANTIP antipassive
APPL applicative
ART article
AUX auxiliary
BEN benefactive
*/
#let A = smallcaps([a])
#let all = smallcaps([all])
#let art = smallcaps([art])
/*
CAUS causative
CLF classifier
COM comitative
COMP complementizer
COMPL completive
COND conditional
COP copula
CVB converb
DAT dative
DECL declarative
DEF definite
DEM demonstrative
DET determiner
DIST distal
*/
#let com = smallcaps([com])
#let dat = smallcaps([dat])
/*
DISTR distributive
DU dual
DUR durative
ERG ergative
EXCL exclusive
F feminine
FOC focus
FUT future
GEN genitive
IMP imperative
INCL inclusive
IND indicative
INDF indefinite
INF infinitive
INS instrumental
INTR intransitive
IPFV imperfective
*/
#let du = smallcaps([du])
#let fut = smallcaps([fut])
#let gen = smallcaps([gen])
#let inf = smallcaps([inf])
#let ins = smallcaps([ins])
/*
IRR irrealis
LOC locative
M masculine
N neuter
N- non- (e.g. NSG nonsingular, NPST nonpast)
NEG negation, negative
NMLZ nominalizer/nominalization
NOM nominative
OBJ object
OBL oblique
P patient-like argument of canonical transitive verb
PASS passive
PFV perfective
PL plural
*/
#let obl = smallcaps([obl])
#let neg = smallcaps([neg])
#let obj = smallcaps([obj])
#let pl = smallcaps([pl])
/*
POSS possessive
PRED predicative
PRF perfect
PRS present
PROG progressive
PROH prohibitive
PROX proximal/proximate
PST past
PTCP participle
PURP purposive
Q question particle/marker
QUOT quotative
RECP reciprocal
REFL reflexive
*/
#let P = smallcaps([p])
#let pos = smallcaps([pos])
#let prog = smallcaps([prog])
#let pst = smallcaps([pst])
/*
REL relative
RES resultative
S single argument of canonical intransitive verb
SBJ subject
SBJV subjunctive
SG singular
TOP topic
TR transitive
VOC vocative
*/
#let sg = smallcaps([sg])
#let sbj = smallcaps([sbj])

View File

@ -1,6 +1,6 @@
[package]
name = "leipzig-glossing"
version = "0.1.0"
version = "0.2.0"
entrypoint = "leipzig-gloss.typ"
authors = ["Greg Shuflin", "Other open-source contributors"]
license = "MIT"