Make octave-reading source attribution bilingual
The heading above each Matins octave reading was a bare Latin string (e.g. "Sermo sancti Bernárdi Abbátis") rendered as-is in both the Latin and English columns, since the app shows both languages side by side rather than toggling. Widen OctaveReadingText.source and the lesson part's label to carry per-language text, and render it as its own lang-columns row like the body text beneath it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L3wxvz3mPXiHxkPB5JpnmD
This commit is contained in:
+8
-2
@@ -232,7 +232,13 @@ export type ResolvedPart =
|
||||
// groups a reading under its 'nocturn-psalmody' sibling for the UI (1-3,
|
||||
// omitted on a ferial 1-nocturn day); `source` is a human-readable
|
||||
// attribution ("St. John Chrysostom, Homily 3 on Matthew"; the user's
|
||||
// own bible-reading plan doesn't carry one). `isGospel` flags a reading
|
||||
// own bible-reading plan doesn't carry one). `label` is usually that same
|
||||
// plain string (nocturn-readings' own `source`, already written once in
|
||||
// English and reused as-is in both language columns), but octave
|
||||
// readings' own `source` is a genuine bilingual attribution (their
|
||||
// English title is a real translation of a Latin incipit, not an
|
||||
// editorial description written once) so `label` accepts either shape —
|
||||
// see hour-view.ts's renderLessonLabel. `isGospel` flags a reading
|
||||
// as a Gospel pericope (from either the user's own continuous-reading
|
||||
// plan or the day's own proper Gospel+homily — never a Common-of-Saints
|
||||
// fallback Gospel, which this app deliberately excludes for Matins, see
|
||||
@@ -245,7 +251,7 @@ export type ResolvedPart =
|
||||
| {
|
||||
kind: 'lesson';
|
||||
text: ResolvedText;
|
||||
label?: string;
|
||||
label?: string | Partial<Record<string, string>>;
|
||||
nocturn?: number;
|
||||
source?: string;
|
||||
isGospel?: boolean;
|
||||
|
||||
@@ -21,9 +21,11 @@ export interface ScriptureCitation {
|
||||
export interface OctaveReadingText {
|
||||
id: string;
|
||||
/** Attribution for the reading — author and work, e.g. "St. John
|
||||
* Damascene, 2nd Sermon on the Dormition of the Mother of God". Not
|
||||
* itself translated/localized. */
|
||||
source: string;
|
||||
* Damascene, 2nd Sermon on the Dormition of the Mother of God". Bilingual
|
||||
* like everything else here since it's rendered as the reading's own
|
||||
* heading in both language columns (2026-08-21 fix: it used to be a bare
|
||||
* Latin incipit shown verbatim in the English column too). */
|
||||
source: Partial<Record<LanguageCode, string>>;
|
||||
text: Partial<Record<LanguageCode, string>>;
|
||||
/** The first responsory following the reading in the source, where one
|
||||
* was found — which one to use when several lessons (and several
|
||||
@@ -51,7 +53,7 @@ export interface OctaveReadingText {
|
||||
*/
|
||||
interface OctaveReadingRecord {
|
||||
id: string;
|
||||
source: string;
|
||||
source: Partial<Record<LanguageCode, string>>;
|
||||
text?: Partial<Record<LanguageCode, string>>;
|
||||
passages?: ScriptureCitation[];
|
||||
responsory?: Partial<Record<LanguageCode, string>>;
|
||||
|
||||
+22
-1
@@ -34,6 +34,27 @@ function renderCitation(text: ResolvedText): string {
|
||||
return values.length ? `<p class="ordo-part-citation">${escapeHtml(values.join(' / '))}</p>` : '';
|
||||
}
|
||||
|
||||
// A lesson's `label` is either a plain string (an editorial attribution
|
||||
// written once and reused in both language columns) or a per-language
|
||||
// object (a genuine translation, e.g. an octave reading's Latin incipit
|
||||
// vs. its English title) — render the latter as its own lang-columns row
|
||||
// matching the body text underneath it.
|
||||
function renderLessonLabel(
|
||||
label: string | Partial<Record<string, string>> | undefined,
|
||||
languages: readonly string[],
|
||||
): string {
|
||||
if (!label) {
|
||||
return '<h3 class="ordo-part-label">Reading</h3>';
|
||||
}
|
||||
if (typeof label === 'string') {
|
||||
return `<h3 class="ordo-part-label">${escapeHtml(label)}</h3>`;
|
||||
}
|
||||
const cols = languages
|
||||
.map((lang) => `<span class="lang-column" lang="${lang}">${escapeHtml(label[lang] ?? '')}</span>`)
|
||||
.join('');
|
||||
return `<h3 class="ordo-part-label lang-columns lang-columns-${languages.length}">${cols}</h3>`;
|
||||
}
|
||||
|
||||
function renderPart(part: ResolvedPart, languages: readonly string[]): string {
|
||||
switch (part.kind) {
|
||||
case 'hymn':
|
||||
@@ -70,7 +91,7 @@ function renderPart(part: ResolvedPart, languages: readonly string[]): string {
|
||||
case 'lesson':
|
||||
return `
|
||||
<section class="ordo-part ordo-part-lesson${part.isGospel ? ' ordo-part-lesson-gospel' : ''}">
|
||||
<h3 class="ordo-part-label">${part.isGospel ? 'Gospel' : part.label ? escapeHtml(part.label) : 'Reading'}</h3>
|
||||
${part.isGospel ? '<h3 class="ordo-part-label">Gospel</h3>' : renderLessonLabel(part.label, languages)}
|
||||
${renderCitation(part.text)}
|
||||
${renderColumns(part.text, languages)}
|
||||
${part.responsory ? `<div class="ordo-part-responsory">${renderColumns(part.responsory, languages)}</div>` : ''}
|
||||
|
||||
Reference in New Issue
Block a user