1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
package bible
import (
"fmt"
"regexp"
"sort"
"strconv"
"strings"
"github.com/lukaszkasprzak/lectio/internal/psalter"
)
// polishToEn maps Polish Biblical abbreviations to the book name accepted by
// the kjv-family tools. Gospels use the short forms (Mat/Mark/Luke/John);
// other books use full names that the tools resolve by unique prefix.
// Ported verbatim from ewangelia.py POLISH_TO_EN.
var polishToEn = map[string]string{
// Old Testament
"Rdz": "Genesis", "Wj": "Exodus", "Kpł": "Leviticus", "Lb": "Numbers",
"Pwt": "Deuteronomy", "Joz": "Joshua", "Sdz": "Judges", "Rt": "Ruth",
"1 Sm": "1 Samuel", "2 Sm": "2 Samuel", "1 Krl": "1 Kings", "2 Krl": "2 Kings",
"1 Krn": "1 Chronicles", "2 Krn": "2 Chronicles", "Ezd": "Ezra",
"Ne": "Nehemiah", "Tb": "Tobit", "Jdt": "Judith", "Est": "Esther",
"1 Mch": "1 Maccabees", "2 Mch": "2 Maccabees", "Hi": "Job", "Ps": "Psalms",
"Prz": "Proverbs", "Koh": "Ecclesiastes", "Pnp": "Song of Solomon",
"Mdr": "Wisdom", "Syr": "Sirach", "Iz": "Isaiah", "Jr": "Jeremiah",
"Lm": "Lamentations", "Ba": "Baruch", "Ez": "Ezekiel", "Dn": "Daniel",
"Oz": "Hosea", "Jl": "Joel", "Am": "Amos", "Ab": "Obadiah", "Jon": "Jonah",
"Mi": "Micah", "Na": "Nahum", "Ha": "Habakkuk", "So": "Zephaniah",
"Ag": "Haggai", "Za": "Zechariah", "Ml": "Malachi",
// New Testament
"Mt": "Mat", "Mk": "Mark", "Łk": "Luke", "J": "John", "Dz": "Acts",
"Rz": "Romans", "1 Kor": "1 Corinthians", "2 Kor": "2 Corinthians",
"Ga": "Galatians", "Ef": "Ephesians", "Flp": "Philippians",
"Kol": "Colossians", "1 Tes": "1 Thessalonians", "2 Tes": "2 Thessalonians",
"1 Tm": "1 Timothy", "2 Tm": "2 Timothy", "Tt": "Titus", "Flm": "Philemon",
"Hbr": "Hebrews", "Jk": "James", "1 P": "1 Peter", "2 P": "2 Peter",
"1 J": "1 John", "2 J": "2 John", "3 J": "3 John", "Jud": "Jude",
"Ap": "Revelation",
}
var (
porRe = regexp.MustCompile(`(?i)^\s*por\.\s*`)
refrainRe = regexp.MustCompile(`\s*\(R\.:.*$`)
wsRe = regexp.MustCompile(`\s+`)
iRe = regexp.MustCompile(`\s+i\s+`)
dashRe = regexp.MustCompile(`\s*[-–—]\s*`)
verseLetterRe = regexp.MustCompile(`(\d)[a-c]\b`)
bookDotRe = regexp.MustCompile(`(\p{L})\.`) // abbreviation dot after a letter ("Cor." -> "Cor")
psalmRestRe = regexp.MustCompile(`(\d+)(?:\s*\((\d+)\))?(.*)$`)
digitsRe = regexp.MustCompile(`\d+`)
)
// polishBookKeys holds the POLISH_TO_EN keys sorted longest-first, matching
// the Python `sorted(POLISH_TO_EN, key=len, reverse=True)` traversal order.
var polishBookKeys = sortedPolishBookKeys()
func sortedPolishBookKeys() []string {
keys := make([]string, 0, len(polishToEn))
for k := range polishToEn {
keys = append(keys, k)
}
sort.SliceStable(keys, func(i, j int) bool { return len(keys[i]) > len(keys[j]) })
return keys
}
// psalmRef maps the psalm citation body ("63 (62), 2. 3-4. ...") to the target Psalter.
func psalmRef(rest, system string) string {
m := psalmRestRe.FindStringSubmatch(rest)
if m == nil {
return rest
}
heb, _ := strconv.Atoi(m[1])
tail := m[3]
if system == "vulgate" {
ch := m[1]
if m[2] != "" {
ch = m[2]
}
return ch + tail
}
if system == "drb" {
tail = digitsRe.ReplaceAllStringFunc(tail, func(s string) string {
n, _ := strconv.Atoi(s)
return strconv.Itoa(psalter.DrbVerse(heb, n))
})
}
return strconv.Itoa(heb) + tail
}
// ToEnglishRef converts a Polish citation to kjv-tool style: "Mt 7, 1-5" -> "Mat 7:1-5".
//
// Handles the extra apparatus of non-gospel readings: a leading "por." (compare)
// marker, a trailing "(R.: ...)" responsorial refrain, and psalm versification.
// system selects the target Psalter: "vulgate" (vul/grb/wuj) uses the citation's
// Vulgate chapter (62 of "63 (62)"); "drb" uses the Hebrew chapter (63) with the
// DRB title-fold verse shift; other values use the Hebrew chapter unshifted.
func ToEnglishRef(plRef, system string) (string, error) {
plRef = porRe.ReplaceAllString(plRef, "") // 'compare' marker
plRef = refrainRe.ReplaceAllString(plRef, "") // responsorial refrain
plRef = wsRe.ReplaceAllString(strings.TrimSpace(plRef), " ")
var book string
found := false
for _, k := range polishBookKeys {
if plRef == k || strings.HasPrefix(plRef, k+" ") {
book = k
found = true
break
}
}
if !found {
return "", fmt.Errorf("unknown book in reference: %q", plRef)
}
rest := strings.TrimSpace(plRef[len(book):])
if polishToEn[book] == "Psalms" {
rest = psalmRef(rest, system)
}
rest = strings.ReplaceAll(rest, ", ", ":") // chapter/verse separator
rest = strings.ReplaceAll(rest, ". ", ",") // disjoint verse groups
rest = iRe.ReplaceAllString(rest, ",") // Polish 'and'
rest = dashRe.ReplaceAllString(rest, "-") // normalise ranges
rest = verseLetterRe.ReplaceAllString(rest, "$1") // drop verse-part letters (15a -> 15)
rest = strings.ReplaceAll(rest, " ", "")
return strings.TrimSpace(fmt.Sprintf("%s %s", polishToEn[book], rest)), nil
}
|