Motivation
A citation system pins one canonical vocabulary for each locator component. For bible-book-chapter-verse that is the OSIS book abbreviations — Gen, John, 1Cor — settled in textrefs/registry#3.
Providers do not all agree with it. Two of the most important scholarly Bible editions address books by USFM code instead:
- Nestle-Aland 28 —
https://www.die-bibel.de/bibel/NA28/JHN.3/#JHN.3.16
- Biblia Hebraica Stuttgartensia —
https://www.die-bibel.de/bibel/BHS/PSA.23/#PSA.23.1
Neither can be expressed today. url: substitutes capture groups verbatim, so it produces .../NA28/John.3/, which die-bibel.de does not recognise. url_by: maps a variable to a whole URL, but the URL still needs {chapter} and {verse} after the book segment, so a per-book map cannot express it either.
The result is that the registry can enumerate the New Testament and the Tanakh but cannot point either at its standard critical edition — the gap the scholarly resolver review called out as the most serious.
Proposed change
A new optional per-resolver vars: field that translates one locator value into the provider's spelling, leaving the rest of the template intact:
- provider: Deutsche Bibelgesellschaft
edition: 'Nestle-Aland, Novum Testamentum Graece, 28th edn (NA28)'
language: grc
access: open
vars:
bookUsfm:
from: book
map:
Matt: MAT
John: JHN
1Cor: 1CO
url: 'https://www.die-bibel.de/bibel/NA28/{bookUsfm}.{chapter}/#{bookUsfm}.{chapter}.{verse}'
Each entry reads the value of from out of the locator-derived variables, looks it up in map, and binds the result under a new name usable in url: or as the url_by: key.
Compile-time only: the mapping produces a different expanded URL and nothing new reaches the published record, so ResolverTargetEntry, standard/schema/, and public/contexts/v1.jsonld are untouched.
Two deliberate rules:
- A value with no entry in the map is treated exactly like a missing template variable — the entry is skipped for that reference and the compiler warns. A hole in a book table then shows up as a non-zero skipped-entry count in the build output instead of quietly emitting a wrong URL. This matters more than it sounds: die-bibel.de answers an unknown book code with HTTP 200 and a default page (NA28 renders Matthäus 1, BHS renders Genesis 1), so a typo would otherwise produce 1,000 silently wrong links rather than an error.
- A
vars name that shadows a locator capture group is rejected, so {book} always means the canonical OSIS code no matter which resolver you are reading.
Alternatives considered
Model USFM as its own CitationSystem. Rejected. USFM codes are a provider's internal book vocabulary, not a citation grammar — nobody writes "JHN.3.16" in a footnote. Registering it as a system would mint a second UUID for every one of the ~31,000 NT and Tanakh verses and duplicate every references_range block, for no gain in citation practice. The same argument would then repeat for every provider with its own book list.
Extend url_by to substitute a fragment rather than a whole URL. Rejected as a silent semantic change to an existing field; url_by means "this provider's URLs are arbitrary" and should keep meaning that. The two now sit side by side: vars when a provider renames part of a URL, url_by when the whole URL is arbitrary.
Per-reference extra_resolvers. Works, but requires hand-writing ~31,000 URLs.
Acceptance criteria
Motivation
A citation system pins one canonical vocabulary for each locator component. For
bible-book-chapter-versethat is the OSIS book abbreviations —Gen,John,1Cor— settled in textrefs/registry#3.Providers do not all agree with it. Two of the most important scholarly Bible editions address books by USFM code instead:
https://www.die-bibel.de/bibel/NA28/JHN.3/#JHN.3.16https://www.die-bibel.de/bibel/BHS/PSA.23/#PSA.23.1Neither can be expressed today.
url:substitutes capture groups verbatim, so it produces.../NA28/John.3/, which die-bibel.de does not recognise.url_by:maps a variable to a whole URL, but the URL still needs{chapter}and{verse}after the book segment, so a per-book map cannot express it either.The result is that the registry can enumerate the New Testament and the Tanakh but cannot point either at its standard critical edition — the gap the scholarly resolver review called out as the most serious.
Proposed change
A new optional per-resolver
vars:field that translates one locator value into the provider's spelling, leaving the rest of the template intact:Each entry reads the value of
fromout of the locator-derived variables, looks it up inmap, and binds the result under a new name usable inurl:or as theurl_by:key.Compile-time only: the mapping produces a different expanded URL and nothing new reaches the published record, so
ResolverTargetEntry,standard/schema/, andpublic/contexts/v1.jsonldare untouched.Two deliberate rules:
varsname that shadows a locator capture group is rejected, so{book}always means the canonical OSIS code no matter which resolver you are reading.Alternatives considered
Model USFM as its own CitationSystem. Rejected. USFM codes are a provider's internal book vocabulary, not a citation grammar — nobody writes "JHN.3.16" in a footnote. Registering it as a system would mint a second UUID for every one of the ~31,000 NT and Tanakh verses and duplicate every
references_rangeblock, for no gain in citation practice. The same argument would then repeat for every provider with its own book list.Extend
url_byto substitute a fragment rather than a whole URL. Rejected as a silent semantic change to an existing field;url_bymeans "this provider's URLs are arbitrary" and should keep meaning that. The two now sit side by side:varswhen a provider renames part of a URL,url_bywhen the whole URL is arbitrary.Per-reference
extra_resolvers. Works, but requires hand-writing ~31,000 URLs.Acceptance criteria
varsparses onResolverEntrySourceand is rejected when malformed (strict object).url:and works as theurl_by:key.varsname colliding with a locator capture group fails the build with a clear message.scripts/compile.test.tscovers all of the above. (Resolver URL expansion has no test coverage at all today, so the pre-existingurlandurl_bybehaviour gets pinned in the same pass.)vars, when to prefer it overurl_by, and the skip-and-warn rule.ResolverTargetEntryshape or the JSON-LD context.