citefind: a note the translator signed is not a reference

Both volumes of *The Decline of the West* went from zero citations to 415
once the arrow marker was recognised — and 167 of those were the editor's and
the translator's own glosses:

    Latin: "nature of the thing." — Ed.
    Reference to the wars between Athens and Sparta. Their dates are
    431-404 BC and 31 BC. — Ed.

An em dash and a role at the very end is how a translator signs a note of
their own. It cites nothing, and it is a convention every academic
translation uses.

391 of the 415 also carried a stray `<!-- -->`: pandoc's list separator, left
in the middle of the reference text. HTML comments are conversion furniture
and are cut with the marker.

And `See R. Hirzel, Die Person (1914), p. 7.` read as an author-date entry
whose authors were "R. Hirzel, Die Person" and whose title was nothing — the
head is all capitalised words and the year is bracketed, which is exactly
what an author-date opening looks like. Where that reading yields no title
and reading it as a note yields one, the note wins. Having a title is how you
can tell which was meant.
This commit is contained in:
funkpower
2026-09-20 16:13:09 -04:00
parent 99cc059310
commit 0db9dabfd3
2 changed files with 64 additions and 0 deletions
+40
View File
@@ -231,6 +231,14 @@ static ACKNOWLEDGEMENT: LazyLock<Regex> = LazyLock::new(|| {
.expect("a valid pattern")
});
/// How a translator or an editor signs a note of their own: an em dash and
/// their role, at the very end. `Latin: "nature of the thing." — Ed.` is a
/// gloss on the text in front of it and cites nothing.
static GLOSS_SIGNATURE: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"(?i)[—–-]{1,2}\s*(?:ed|tr|trans|transl|a\.\s*n|au)\.?\s*$")
.expect("a valid pattern")
});
/// A page reference or a bracketed imprint: what a citation has and a remark
/// about the argument does not.
static CITATION_ANCHOR: LazyLock<Regex> = LazyLock::new(|| {
@@ -358,6 +366,7 @@ pub fn one(text: &str, marker: Option<&str>) -> Option<Reference> {
|| BIOGRAPHY.is_match(text)
|| PRINT_FURNITURE.is_match(text)
|| ACKNOWLEDGEMENT.is_match(text)
|| GLOSS_SIGNATURE.is_match(text)
{
return None;
}
@@ -481,6 +490,21 @@ pub fn one(text: &str, marker: Option<&str>) -> Option<Reference> {
if out.title.as_deref().is_some_and(str::is_empty) {
out.title = None;
}
// `See R. Hirzel, Die Person (1914), p. 7.` reads as an author-date entry
// — the head is all capitalised words and the year is in brackets — and
// then yields no title, because the title is sitting inside what was
// taken for the names. A note that names a work is the better reading of
// the same line, and having a title is how you can tell.
if out.title.is_none() {
if let Some((names, title, container)) = note_form(text) {
if title.is_some() {
out.authors = names;
out.title = title;
out.container = container.or(out.container.take());
}
}
}
out.kind = kind(text, &out);
Some(out)
@@ -1483,6 +1507,22 @@ mod tests {
assert_eq!(found.year, Some(2000));
}
#[test]
fn a_translators_note_signs_itself_and_cites_nothing() {
for gloss in [
"Latin: \"nature of the thing.\" — Ed.",
"Reference to the wars between Athens and Sparta, 431-404 BC. — Ed.",
"Meaning here names, dates and numbers, in the usual sense. — Tr.",
] {
assert!(one(gloss, None).is_none(), "{gloss}");
}
// A note that cites a work is still a citation, sign-off or not.
let real = read("See R. Hirzel, Die Person (1914), p. 7.");
assert_eq!(real.authors, ["R. Hirzel"]);
assert_eq!(real.title.as_deref(), Some("Die Person"));
assert_eq!(real.year, Some(1914));
}
#[test]
fn a_short_note_names_a_work_without_a_year_or_a_publisher() {
// The form a history book uses for every citation after the first:
+24
View File
@@ -112,6 +112,11 @@ static ARROW_MARKER: LazyLock<Regex> = LazyLock::new(|| {
.expect("a valid pattern")
});
/// An HTML comment. Conversion leaves `<!-- -->` between list items and
/// `<!-- Page 41 -->` where a page turned; neither is part of a reference.
static COMMENT: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"<!--.*?-->").expect("a valid pattern"));
/// A bullet or a blockquote marker, which conversion puts in front of a note
/// often enough to matter. A leading `*` is left alone: it opens an italic
/// title as often as it opens a list.
@@ -276,6 +281,7 @@ fn unnumbered(text: &str) -> Vec<Entry> {
/// further along survives.
fn strip_marker(raw: &str) -> Entry {
let text = cut_footer(&tidy(raw));
let text = tidy(&COMMENT.replace_all(&text, " "));
let text = BULLET.replace(&text, "").to_string();
let (bare, from) = visible(&text, 160);
@@ -517,6 +523,24 @@ Another Anonymous Work. Cambridge, 2001.";
assert_eq!(entry.text, "Holquist, Making War, pp. 211-12.");
}
#[test]
fn the_back_reference_marker_an_epub_conversion_leaves() {
let plain = strip_marker("\\[←788 \\]\u{a0} Krumbacher, Byz. Lit.-Gesch., p. 969.");
assert_eq!(plain.marker.as_deref(), Some("788"));
assert_eq!(plain.text, "Krumbacher, Byz. Lit.-Gesch., p. 969.");
let linked =
strip_marker("\\[[←193](index_split_011.xhtml#citation-193) \\]\u{a0} Œd. Rex., 642.");
assert_eq!(linked.marker.as_deref(), Some("193"));
assert_eq!(linked.text, "Œd. Rex., 642.");
}
#[test]
fn a_comment_conversion_left_behind_is_not_part_of_a_reference() {
let entry = strip_marker("1. Smith, *A Title* (Oxford, 1998). <!-- -->");
assert_eq!(entry.text, "Smith, *A Title* (Oxford, 1998).");
}
#[test]
fn an_entry_with_no_marker_keeps_all_of_itself() {
let entry = strip_marker("Smith, John. The Title. Oxford, 1998.");