feat(faith): adopt flat-id romcal fork and simplify 1962 calendar rendering
Switch the romcal dependency to AlexBocken/romcal (monorepo fork with
collapsed bucket prefixes) and strip the runtime prefix-fallback chain
from liturgicalCalendar.ts — name/propers lookups now use a single
flat id. The 1962 data model shrinks to just what the rendering uses
(commem {id,name}, detail carrying propers as {key, la[], local[]})
and the detail + overview pages drop the rubrics/octave/properSource
fields that never got wired in.
This commit is contained in:
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "homepage",
|
"name": "homepage",
|
||||||
"version": "1.35.0",
|
"version": "1.35.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -62,7 +62,7 @@
|
|||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"mongoose": "^9.4.1",
|
"mongoose": "^9.4.1",
|
||||||
"node-cron": "^4.2.1",
|
"node-cron": "^4.2.1",
|
||||||
"romcal": "github:AlexBocken/romcal1962#dev",
|
"romcal": "github:AlexBocken/romcal#dev",
|
||||||
"sharp": "^0.34.5",
|
"sharp": "^0.34.5",
|
||||||
"web-haptics": "^0.0.6"
|
"web-haptics": "^0.0.6"
|
||||||
},
|
},
|
||||||
|
|||||||
Generated
+8
-8
@@ -22,7 +22,7 @@ importers:
|
|||||||
version: 2.1.8-no-fsevents.3
|
version: 2.1.8-no-fsevents.3
|
||||||
'@romcal/calendar.general-roman':
|
'@romcal/calendar.general-roman':
|
||||||
specifier: 3.0.0-dev.125
|
specifier: 3.0.0-dev.125
|
||||||
version: 3.0.0-dev.125(romcal@https://codeload.github.com/AlexBocken/romcal1962/tar.gz/c353a43b08514f4af99e3a6796042155557f023d(typescript@6.0.2))
|
version: 3.0.0-dev.125(romcal@https://codeload.github.com/AlexBocken/romcal/tar.gz/efbaab5813ecbdd4956d357c3f3a9bf06b6e1dde(typescript@6.0.2))
|
||||||
'@sveltejs/adapter-node':
|
'@sveltejs/adapter-node':
|
||||||
specifier: ^5.5.4
|
specifier: ^5.5.4
|
||||||
version: 5.5.4(@sveltejs/kit@2.56.1(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.1)(vite@8.0.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.1)(@types/node@22.18.0)(esbuild@0.27.3)(terser@5.46.0)))(svelte@5.55.1)(typescript@6.0.2)(vite@8.0.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.1)(@types/node@22.18.0)(esbuild@0.27.3)(terser@5.46.0)))
|
version: 5.5.4(@sveltejs/kit@2.56.1(@sveltejs/vite-plugin-svelte@7.0.0(svelte@5.55.1)(vite@8.0.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.1)(@types/node@22.18.0)(esbuild@0.27.3)(terser@5.46.0)))(svelte@5.55.1)(typescript@6.0.2)(vite@8.0.4(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.1)(@types/node@22.18.0)(esbuild@0.27.3)(terser@5.46.0)))
|
||||||
@@ -54,8 +54,8 @@ importers:
|
|||||||
specifier: ^4.2.1
|
specifier: ^4.2.1
|
||||||
version: 4.2.1
|
version: 4.2.1
|
||||||
romcal:
|
romcal:
|
||||||
specifier: github:AlexBocken/romcal1962#dev
|
specifier: github:AlexBocken/romcal#dev
|
||||||
version: https://codeload.github.com/AlexBocken/romcal1962/tar.gz/c353a43b08514f4af99e3a6796042155557f023d(typescript@6.0.2)
|
version: https://codeload.github.com/AlexBocken/romcal/tar.gz/efbaab5813ecbdd4956d357c3f3a9bf06b6e1dde(typescript@6.0.2)
|
||||||
sharp:
|
sharp:
|
||||||
specifier: ^0.34.5
|
specifier: ^0.34.5
|
||||||
version: 0.34.5
|
version: 0.34.5
|
||||||
@@ -1782,8 +1782,8 @@ packages:
|
|||||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
romcal@https://codeload.github.com/AlexBocken/romcal1962/tar.gz/c353a43b08514f4af99e3a6796042155557f023d:
|
romcal@https://codeload.github.com/AlexBocken/romcal/tar.gz/efbaab5813ecbdd4956d357c3f3a9bf06b6e1dde:
|
||||||
resolution: {tarball: https://codeload.github.com/AlexBocken/romcal1962/tar.gz/c353a43b08514f4af99e3a6796042155557f023d}
|
resolution: {tarball: https://codeload.github.com/AlexBocken/romcal/tar.gz/efbaab5813ecbdd4956d357c3f3a9bf06b6e1dde}
|
||||||
version: 3.0.0-dev.125
|
version: 3.0.0-dev.125
|
||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
@@ -2683,9 +2683,9 @@ snapshots:
|
|||||||
'@rollup/rollup-win32-x64-msvc@4.60.1':
|
'@rollup/rollup-win32-x64-msvc@4.60.1':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@romcal/calendar.general-roman@3.0.0-dev.125(romcal@https://codeload.github.com/AlexBocken/romcal1962/tar.gz/c353a43b08514f4af99e3a6796042155557f023d(typescript@6.0.2))':
|
'@romcal/calendar.general-roman@3.0.0-dev.125(romcal@https://codeload.github.com/AlexBocken/romcal/tar.gz/efbaab5813ecbdd4956d357c3f3a9bf06b6e1dde(typescript@6.0.2))':
|
||||||
dependencies:
|
dependencies:
|
||||||
romcal: https://codeload.github.com/AlexBocken/romcal1962/tar.gz/c353a43b08514f4af99e3a6796042155557f023d(typescript@6.0.2)
|
romcal: https://codeload.github.com/AlexBocken/romcal/tar.gz/efbaab5813ecbdd4956d357c3f3a9bf06b6e1dde(typescript@6.0.2)
|
||||||
|
|
||||||
'@sec-ant/readable-stream@0.4.1': {}
|
'@sec-ant/readable-stream@0.4.1': {}
|
||||||
|
|
||||||
@@ -3545,7 +3545,7 @@ snapshots:
|
|||||||
'@rollup/rollup-win32-x64-msvc': 4.60.1
|
'@rollup/rollup-win32-x64-msvc': 4.60.1
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
|
|
||||||
romcal@https://codeload.github.com/AlexBocken/romcal1962/tar.gz/c353a43b08514f4af99e3a6796042155557f023d(typescript@6.0.2):
|
romcal@https://codeload.github.com/AlexBocken/romcal/tar.gz/efbaab5813ecbdd4956d357c3f3a9bf06b6e1dde(typescript@6.0.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
i18next: 26.0.4(typescript@6.0.2)
|
i18next: 26.0.4(typescript@6.0.2)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
|
|||||||
@@ -35,52 +35,25 @@ export interface SeasonArc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Rite1962Commem {
|
export interface Rite1962Commem {
|
||||||
key: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
rankName: string;
|
|
||||||
kind: 'tempora' | 'sancti';
|
|
||||||
colorNames: string[];
|
|
||||||
colorKeys: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Rite1962Detail {
|
export interface Rite1962Detail {
|
||||||
class: 1 | 2 | 3 | 4;
|
class: 1 | 2 | 3 | 4;
|
||||||
kind: 'tempora' | 'sancti';
|
kind: 'tempora' | 'sancti';
|
||||||
commemorations: Rite1962Commem[];
|
commemorations: Rite1962Commem[];
|
||||||
rubrics: {
|
|
||||||
gloria: boolean;
|
|
||||||
credo: boolean;
|
|
||||||
preface?: string;
|
|
||||||
lastGospel?: string;
|
|
||||||
ite?: string;
|
|
||||||
};
|
|
||||||
octave?: {
|
octave?: {
|
||||||
id: string;
|
ofId: string;
|
||||||
parentFeastId: string;
|
|
||||||
day: number;
|
day: number;
|
||||||
rank: string;
|
|
||||||
};
|
};
|
||||||
vigilOf?: string;
|
vigilOf?: string;
|
||||||
transferredFrom?: string;
|
transferredFrom?: string;
|
||||||
properSource: string;
|
|
||||||
communeSlug?: string;
|
|
||||||
propers: ProperSection[];
|
propers: ProperSection[];
|
||||||
extraSections: ProperSection[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ProperSegment {
|
|
||||||
refs: string[];
|
|
||||||
la: string;
|
|
||||||
local?: string;
|
|
||||||
// When true, `local` text comes from the Bible translation lookup because
|
|
||||||
// the propers dataset had no localized text for this segment.
|
|
||||||
fromBible?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProperSection {
|
export interface ProperSection {
|
||||||
key: string;
|
key: string;
|
||||||
segments: ProperSegment[];
|
la: string[];
|
||||||
// Aggregate list of refs across segments (for quick checks)
|
local: string[];
|
||||||
refs: string[];
|
|
||||||
fromBible?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,14 +19,11 @@ import {
|
|||||||
Switzerland_Saint_Maurice_Abbey,
|
Switzerland_Saint_Maurice_Abbey,
|
||||||
Switzerland_Sankt_Gallen,
|
Switzerland_Sankt_Gallen,
|
||||||
Switzerland_Sion,
|
Switzerland_Sion,
|
||||||
resolvePropersBlocks
|
createI18n1962
|
||||||
} from 'romcal/1962';
|
|
||||||
import type {
|
|
||||||
LiturgicalDay1962,
|
|
||||||
MassPropersBlocks,
|
|
||||||
MassSectionField,
|
|
||||||
PropersBlock
|
|
||||||
} from 'romcal/1962';
|
} from 'romcal/1962';
|
||||||
|
import type { LiturgicalDay1962, RomcalBundle1962 } from 'romcal/1962';
|
||||||
|
import { pathToFileURL } from 'node:url';
|
||||||
|
import { resolve as resolvePath } from 'node:path';
|
||||||
import {
|
import {
|
||||||
colorLabel1962,
|
colorLabel1962,
|
||||||
rank1962Label,
|
rank1962Label,
|
||||||
@@ -38,13 +35,9 @@ import {
|
|||||||
import type {
|
import type {
|
||||||
CalendarDay,
|
CalendarDay,
|
||||||
ProperSection,
|
ProperSection,
|
||||||
ProperSegment,
|
|
||||||
Rite1962Commem,
|
Rite1962Commem,
|
||||||
Rite1962Detail
|
Rite1962Detail
|
||||||
} from '../calendarTypes';
|
} from '../calendarTypes';
|
||||||
import { lookupReference } from '$lib/server/bible';
|
|
||||||
import { translateRefToTarget } from '$lib/server/bibleRefLatin';
|
|
||||||
import { resolve as resolvePath } from 'path';
|
|
||||||
|
|
||||||
const bundles1969: Record<Diocese1969, Record<CalendarLang, RomcalBundleObject>> = {
|
const bundles1969: Record<Diocese1969, Record<CalendarLang, RomcalBundleObject>> = {
|
||||||
general: { en: GeneralRoman_En, de: GeneralRoman_De, la: GeneralRoman_La },
|
general: { en: GeneralRoman_En, de: GeneralRoman_De, la: GeneralRoman_La },
|
||||||
@@ -56,7 +49,7 @@ function getRomcal(lang: CalendarLang, diocese: Diocese1969): Romcal {
|
|||||||
const key = `${diocese}|${lang}`;
|
const key = `${diocese}|${lang}`;
|
||||||
let r = romcalByKey.get(key);
|
let r = romcalByKey.get(key);
|
||||||
if (r) return r;
|
if (r) return r;
|
||||||
r = new Romcal({ localizedCalendar: bundles1969[diocese][lang] });
|
r = new Romcal({ localizedCalendar: bundles1969[diocese][lang], scope: 'liturgical' });
|
||||||
romcalByKey.set(key, r);
|
romcalByKey.set(key, r);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
@@ -111,307 +104,183 @@ const calendars1962 = {
|
|||||||
sion: Switzerland_Sion
|
sion: Switzerland_Sion
|
||||||
} as const satisfies Record<Diocese1962, unknown>;
|
} as const satisfies Record<Diocese1962, unknown>;
|
||||||
|
|
||||||
const romcal1962ByKey = new Map<string, Romcal1962>();
|
// Names & propers for the 1962 rite ship as bundled JS files outside the
|
||||||
function getRomcal1962(lang: CalendarLang, diocese: Diocese1962): Romcal1962 {
|
// package's `exports` map, so resolve them via an absolute file URL.
|
||||||
const key = `${diocese}|${lang}`;
|
const bundle1962Cache = new Map<CalendarLang, Promise<RomcalBundle1962>>();
|
||||||
let r = romcal1962ByKey.get(key);
|
function loadBundle1962(lang: CalendarLang): Promise<RomcalBundle1962> {
|
||||||
if (r) return r;
|
let p = bundle1962Cache.get(lang);
|
||||||
// Package localizes celebration.name via i18next; structured proper
|
if (p) return p;
|
||||||
// blocks are fetched lazily via resolvePropersBlocks (below), so we
|
const abs = resolvePath(
|
||||||
// don't ask attachPropers to pre-concatenate locale text.
|
`node_modules/romcal/rites/roman1962/dist/bundles/${lang}/esm/index.js`
|
||||||
const calendar = calendars1962[diocese];
|
);
|
||||||
r = calendar
|
p = import(/* @vite-ignore */ pathToFileURL(abs).href).then(
|
||||||
? new Romcal1962({ localeId: lang, calendar })
|
(m) => m.default as RomcalBundle1962
|
||||||
: new Romcal1962({ localeId: lang });
|
);
|
||||||
romcal1962ByKey.set(key, r);
|
bundle1962Cache.set(lang, p);
|
||||||
return r;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PROPER_ORDER: MassSectionField[] = [
|
const romcal1962ByKey = new Map<string, Promise<Romcal1962>>();
|
||||||
'introit',
|
function getRomcal1962(lang: CalendarLang, diocese: Diocese1962): Promise<Romcal1962> {
|
||||||
'collect',
|
const key = `${diocese}|${lang}`;
|
||||||
'epistle',
|
let p = romcal1962ByKey.get(key);
|
||||||
'gradual',
|
if (p) return p;
|
||||||
'alleluia',
|
const calendar = calendars1962[diocese];
|
||||||
'tract',
|
// `localizedCalendar` must be a 1969-shape bundle; the 1962 names live on
|
||||||
'sequence',
|
// the 1962 propers bundle and are injected via createI18n1962 extraNames.
|
||||||
'gospel',
|
const base1969 = bundles1969.general[lang];
|
||||||
'offertory',
|
p = loadBundle1962(lang).then((b) => {
|
||||||
'secret',
|
const i18next = createI18n1962(lang, { [lang]: b.i18n.names });
|
||||||
'preface',
|
// `i18next` is part of Romcal's runtime config but absent from the
|
||||||
'communion',
|
// published input type. Build via a permissive record so TS accepts it.
|
||||||
'postcommunion'
|
const base: Record<string, unknown> = {
|
||||||
];
|
i18next,
|
||||||
|
localizedCalendar: base1969,
|
||||||
const COLOR_KEY_1962: Record<string, string> = {
|
scope: 'liturgical'
|
||||||
White: 'WHITE',
|
|
||||||
Red: 'RED',
|
|
||||||
Green: 'GREEN',
|
|
||||||
Violet: 'PURPLE',
|
|
||||||
Black: 'BLACK',
|
|
||||||
Rose: 'ROSE'
|
|
||||||
};
|
};
|
||||||
|
if (calendar) base.particularCalendar = calendar;
|
||||||
|
return new Romcal1962(base as ConstructorParameters<typeof Romcal1962>[0]);
|
||||||
|
});
|
||||||
|
romcal1962ByKey.set(key, p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Section order follows the flow of the Mass. The new propers bundles key
|
||||||
|
// sections by their Latin section names.
|
||||||
|
const PROPER_ORDER = [
|
||||||
|
'Introitus',
|
||||||
|
'Oratio',
|
||||||
|
'Lectio',
|
||||||
|
'Graduale',
|
||||||
|
'GradualeF',
|
||||||
|
'Tractus',
|
||||||
|
'Sequentia',
|
||||||
|
'Evangelium',
|
||||||
|
'Offertorium',
|
||||||
|
'Secreta',
|
||||||
|
'Communio',
|
||||||
|
'Postcommunio'
|
||||||
|
] as const;
|
||||||
|
const PROPER_ORDER_SET: ReadonlySet<string> = new Set<string>(PROPER_ORDER);
|
||||||
|
|
||||||
const RANK_FROM_CLASS_1962: Record<1 | 2 | 3 | 4, string> = {
|
const RANK_FROM_CLASS_1962: Record<1 | 2 | 3 | 4, string> = {
|
||||||
1: 'SOLEMNITY',
|
1: 'ClassI',
|
||||||
2: 'FEAST',
|
2: 'ClassII',
|
||||||
3: 'MEMORIAL',
|
3: 'ClassIII',
|
||||||
4: 'WEEKDAY'
|
4: 'ClassIV'
|
||||||
};
|
};
|
||||||
|
|
||||||
function colorKeysFrom(c: LiturgicalDay1962): string[] {
|
function colorKeysFrom(c: LiturgicalDay1962): string[] {
|
||||||
return c.colors.map((col) => COLOR_KEY_1962[col] ?? col.toUpperCase());
|
// romcal 3 returns SCREAMING_SNAKE color keys ("WHITE", "ROSE", ...) which
|
||||||
|
// already match our legend/CSS tokens.
|
||||||
|
return c.colors ? [...c.colors] : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function adaptCommem(c: LiturgicalDay1962, lang: CalendarLang): Rite1962Commem {
|
function buildCommemorations(d: LiturgicalDay1962): Rite1962Commem[] {
|
||||||
const colorKeys = colorKeysFrom(c);
|
return (d.commemorations ?? []).map((c) => ({ id: c.id, name: c.name }));
|
||||||
return {
|
|
||||||
key: c.key,
|
|
||||||
name: c.name,
|
|
||||||
rankName: rank1962Label(c.rank1962, lang),
|
|
||||||
kind: c.kind,
|
|
||||||
colorKeys,
|
|
||||||
colorNames: colorKeys.map((k) => colorLabel1962(k, lang))
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function bibleTextFor(ref: string, targetLang: 'en' | 'de'): string | null {
|
function sectionsFromBundle(
|
||||||
const tsvPath = resolvePath(targetLang === 'de' ? 'static/allioli.tsv' : 'static/drb.tsv');
|
laPropers: Record<string, string[]> | undefined,
|
||||||
const segments = ref.split(';').map((s) => s.trim()).filter(Boolean);
|
localPropers: Record<string, string[]> | undefined
|
||||||
if (!segments.length) return null;
|
|
||||||
|
|
||||||
let lastBook: string | null = null;
|
|
||||||
let lastChapter: string | null = null;
|
|
||||||
const parts: string[] = [];
|
|
||||||
|
|
||||||
for (const seg of segments) {
|
|
||||||
// Detect optional leading book (letters, optional leading digit like "1 Cor")
|
|
||||||
const bookMatch = seg.match(/^(\d?\s?[A-Za-z]+\.?)\s+(.*)$/);
|
|
||||||
let book: string | null = null;
|
|
||||||
let rest = seg;
|
|
||||||
if (bookMatch) {
|
|
||||||
book = bookMatch[1];
|
|
||||||
rest = bookMatch[2].trim();
|
|
||||||
}
|
|
||||||
if (book) lastBook = book;
|
|
||||||
if (!lastBook) continue;
|
|
||||||
|
|
||||||
let chapter: string;
|
|
||||||
let verseRange: string;
|
|
||||||
// Accept "118:85", "118, 85", "118:85-90", or bare "85" (inherit chapter)
|
|
||||||
const normalized = rest.replace(/\s*,\s*/, ':').replace(/\s+/g, ' ').trim();
|
|
||||||
if (normalized.includes(':')) {
|
|
||||||
const [c, v] = normalized.split(':');
|
|
||||||
chapter = c.trim();
|
|
||||||
verseRange = v.trim();
|
|
||||||
lastChapter = chapter;
|
|
||||||
} else if (lastChapter) {
|
|
||||||
chapter = lastChapter;
|
|
||||||
verseRange = normalized;
|
|
||||||
} else continue;
|
|
||||||
|
|
||||||
const fullRef = `${lastBook} ${chapter}:${verseRange}`;
|
|
||||||
const translated = translateRefToTarget(fullRef, targetLang);
|
|
||||||
if (!translated) continue;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const result = lookupReference(translated, tsvPath);
|
|
||||||
if (result && result.verses.length) {
|
|
||||||
parts.push(result.verses.map((v) => `${v.verse}. ${v.text}`).join(' '));
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// skip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return parts.length ? parts.join(' ') : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RawSegment {
|
|
||||||
refs: string[];
|
|
||||||
la: string;
|
|
||||||
local: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zip la / local text streams by index so la[i] and local[i] land in
|
|
||||||
// the same segment. Scripture refs attach to the la block that follows
|
|
||||||
// them; trailing refs with no following la block attach to the last
|
|
||||||
// segment so they still render.
|
|
||||||
function buildSegments(items: PropersBlock, localLang: CalendarLang): RawSegment[] {
|
|
||||||
const la: string[] = [];
|
|
||||||
const local: string[] = [];
|
|
||||||
const refsByIdx = new Map<number, string[]>();
|
|
||||||
let pendingRefs: string[] = [];
|
|
||||||
|
|
||||||
for (const it of items) {
|
|
||||||
if (it.type === 'scriptureRef') {
|
|
||||||
pendingRefs.push(it.ref);
|
|
||||||
} else if (it.type === 'text') {
|
|
||||||
const val = it.value.trim();
|
|
||||||
if (!val) continue;
|
|
||||||
if (it.lang === 'la') {
|
|
||||||
if (pendingRefs.length) {
|
|
||||||
refsByIdx.set(la.length, pendingRefs);
|
|
||||||
pendingRefs = [];
|
|
||||||
}
|
|
||||||
la.push(val);
|
|
||||||
} else if (it.lang === localLang) {
|
|
||||||
local.push(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pendingRefs.length && la.length) {
|
|
||||||
const lastIdx = la.length - 1;
|
|
||||||
const existing = refsByIdx.get(lastIdx) ?? [];
|
|
||||||
refsByIdx.set(lastIdx, [...existing, ...pendingRefs]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const count = Math.max(la.length, local.length);
|
|
||||||
const segs: RawSegment[] = [];
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
segs.push({ refs: refsByIdx.get(i) ?? [], la: la[i] ?? '', local: local[i] ?? '' });
|
|
||||||
}
|
|
||||||
return segs;
|
|
||||||
}
|
|
||||||
|
|
||||||
function propersOf(sections: MassPropersBlocks, lang: CalendarLang): ProperSection[] {
|
|
||||||
const out: ProperSection[] = [];
|
|
||||||
for (const key of PROPER_ORDER) {
|
|
||||||
const block = sections[key];
|
|
||||||
if (!block || !block.length) continue;
|
|
||||||
const rawSegs = buildSegments(block, lang);
|
|
||||||
if (!rawSegs.length) continue;
|
|
||||||
|
|
||||||
const segments: ProperSegment[] = [];
|
|
||||||
const allRefs: string[] = [];
|
|
||||||
let sectionFromBible = false;
|
|
||||||
|
|
||||||
for (const raw of rawSegs) {
|
|
||||||
const seg: ProperSegment = { refs: raw.refs, la: raw.la };
|
|
||||||
if (lang !== 'la' && raw.local) seg.local = raw.local;
|
|
||||||
|
|
||||||
// Bible fallback: only for this segment, using only its own refs
|
|
||||||
if (!seg.local && raw.refs.length && lang !== 'la') {
|
|
||||||
const bible = bibleTextFor(raw.refs.join('; '), lang);
|
|
||||||
if (bible) {
|
|
||||||
seg.local = bible;
|
|
||||||
seg.fromBible = true;
|
|
||||||
sectionFromBible = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allRefs.push(...raw.refs);
|
|
||||||
if (seg.la || seg.local || seg.refs.length) segments.push(seg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!segments.length) continue;
|
|
||||||
const section: ProperSection = { key, segments, refs: allRefs };
|
|
||||||
if (sectionFromBible) section.fromBible = true;
|
|
||||||
out.push(section);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
function extraSectionsOf(
|
|
||||||
extras: Record<string, PropersBlock>,
|
|
||||||
lang: CalendarLang
|
|
||||||
): ProperSection[] {
|
): ProperSection[] {
|
||||||
const out: ProperSection[] = [];
|
if (!laPropers && !localPropers) return [];
|
||||||
for (const [key, block] of Object.entries(extras)) {
|
const sections: ProperSection[] = [];
|
||||||
const buckets: Record<string, string[]> = {};
|
const seen = new Set<string>();
|
||||||
const refs: string[] = [];
|
const emit = (key: string) => {
|
||||||
for (const item of block) {
|
if (seen.has(key)) return;
|
||||||
if (item.type === 'text') (buckets[item.lang] ??= []).push(item.value);
|
seen.add(key);
|
||||||
else if (item.type === 'scriptureRef') refs.push(item.ref);
|
const la = laPropers?.[key];
|
||||||
}
|
const local = localPropers?.[key];
|
||||||
const la = (buckets['la'] ?? []).join('\n\n').trim();
|
if ((!la || !la.length) && (!local || !local.length)) return;
|
||||||
const local = lang === 'la' ? '' : (buckets[lang] ?? []).join('\n\n').trim();
|
sections.push({ key, la: la ? [...la] : [], local: local ? [...local] : [] });
|
||||||
if (!la && !local && refs.length === 0) continue;
|
|
||||||
const segment: ProperSegment = { refs, la };
|
|
||||||
if (local) segment.local = local;
|
|
||||||
out.push({ key, segments: [segment], refs });
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Primary celebration may only carry the sections that override the commune
|
|
||||||
// (e.g. only `gospel` for a confessor). Merge commune sections under the
|
|
||||||
// primary so every liturgical slot has a text source.
|
|
||||||
function mergeCommunePropers(
|
|
||||||
p: LiturgicalDay1962,
|
|
||||||
sections: MassPropersBlocks,
|
|
||||||
extraSections: Record<string, PropersBlock>
|
|
||||||
): { sections: MassPropersBlocks; extraSections: Record<string, PropersBlock> } {
|
|
||||||
const slug = p.properRef.communeSlug;
|
|
||||||
if (!slug) return { sections, extraSections };
|
|
||||||
const communeCelebration = {
|
|
||||||
...p,
|
|
||||||
properRef: { source: `commune/${slug}` }
|
|
||||||
} as LiturgicalDay1962;
|
|
||||||
let communeResolved;
|
|
||||||
try {
|
|
||||||
communeResolved = resolvePropersBlocks(communeCelebration);
|
|
||||||
} catch {
|
|
||||||
return { sections, extraSections };
|
|
||||||
}
|
|
||||||
const mergedSections = { ...communeResolved.sections, ...sections } as MassPropersBlocks;
|
|
||||||
const mergedExtras = { ...communeResolved.extraSections, ...extraSections };
|
|
||||||
return { sections: mergedSections, extraSections: mergedExtras };
|
|
||||||
}
|
|
||||||
|
|
||||||
function adaptDay1962(entries: LiturgicalDay1962[], lang: CalendarLang): CalendarDay {
|
|
||||||
const p = entries[0];
|
|
||||||
const commemorations = entries.slice(1);
|
|
||||||
const colorKeys = colorKeysFrom(p);
|
|
||||||
const colorNames = colorKeys.map((k) => colorLabel1962(k, lang));
|
|
||||||
const resolved = resolvePropersBlocks(p);
|
|
||||||
const { sections, extraSections } = mergeCommunePropers(
|
|
||||||
p,
|
|
||||||
resolved.sections,
|
|
||||||
resolved.extraSections
|
|
||||||
);
|
|
||||||
const detail: Rite1962Detail = {
|
|
||||||
class: p.classOf1962,
|
|
||||||
kind: p.kind,
|
|
||||||
commemorations: commemorations.map((c) => adaptCommem(c, lang)),
|
|
||||||
rubrics: {
|
|
||||||
gloria: p.rubrics.gloria,
|
|
||||||
credo: p.rubrics.credo,
|
|
||||||
preface: p.rubrics.preface,
|
|
||||||
lastGospel: p.rubrics.lastGospel,
|
|
||||||
ite: p.rubrics.ite
|
|
||||||
},
|
|
||||||
...(p.octave
|
|
||||||
? {
|
|
||||||
octave: {
|
|
||||||
id: p.octave.id,
|
|
||||||
parentFeastId: p.octave.parentFeastId,
|
|
||||||
day: p.octave.day,
|
|
||||||
rank: p.octave.rank
|
|
||||||
}
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
...(p.vigil ? { vigilOf: p.vigil.of } : {}),
|
|
||||||
...(p.transferredFromDate ? { transferredFrom: p.transferredFromDate } : {}),
|
|
||||||
properSource: p.properRef.source,
|
|
||||||
...(p.properRef.communeSlug ? { communeSlug: p.properRef.communeSlug } : {}),
|
|
||||||
propers: propersOf(sections, lang),
|
|
||||||
extraSections: extraSectionsOf(extraSections, lang)
|
|
||||||
};
|
};
|
||||||
// Pentecost octave (Pentecost Sunday + 6 days) is carved out of Paschaltide so
|
for (const key of PROPER_ORDER) emit(key);
|
||||||
// it shows as its own arc in the year ring, mirroring the Easter Week octave.
|
// Emit any remaining sections (ember-day readings, extras) in source order.
|
||||||
const isPentecostWeek = typeof p.key === 'string' && p.key.startsWith('easter_time_7_');
|
const extraKeys = new Set<string>();
|
||||||
const seasonKey = isPentecostWeek ? 'Pentecost' : p.season ?? null;
|
for (const k of Object.keys(laPropers ?? {})) if (!PROPER_ORDER_SET.has(k)) extraKeys.add(k);
|
||||||
const seasonNames = seasonKey ? [season1962Label(seasonKey, lang)] : [];
|
for (const k of Object.keys(localPropers ?? {})) if (!PROPER_ORDER_SET.has(k)) extraKeys.add(k);
|
||||||
|
for (const k of extraKeys) emit(k);
|
||||||
|
return sections;
|
||||||
|
}
|
||||||
|
|
||||||
|
function findPropersFor(
|
||||||
|
d: LiturgicalDay1962,
|
||||||
|
bundle: RomcalBundle1962
|
||||||
|
): Record<string, string[]> | undefined {
|
||||||
|
const kind = d.kind1962;
|
||||||
|
const key = d.key1962 ?? d.id;
|
||||||
|
if (!kind) return undefined;
|
||||||
|
return bundle.propers[kind]?.[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
function humanizeId(id: string): string {
|
||||||
|
return id
|
||||||
|
.split(/[_\s]+/)
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
|
||||||
|
.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
// When romcal's own i18next lookup misses (stale bundle, unknown id), it
|
||||||
|
// returns the raw key — detect that and fall back to a direct bundle name
|
||||||
|
// lookup, then a humanized id.
|
||||||
|
function resolveName1962(
|
||||||
|
d: LiturgicalDay1962,
|
||||||
|
localBundle: RomcalBundle1962 | null,
|
||||||
|
laBundle: RomcalBundle1962
|
||||||
|
): string {
|
||||||
|
const raw = d.name ?? '';
|
||||||
|
const id = d.id;
|
||||||
|
const key = d.key1962 ?? d.id;
|
||||||
|
const looksUnresolved =
|
||||||
|
!raw || raw === id || raw === key || /^[a-z][a-z0-9_]*\.[a-z_]+$/.test(raw);
|
||||||
|
if (!looksUnresolved) return raw;
|
||||||
|
const bundles = [localBundle, laBundle].filter(
|
||||||
|
(b): b is RomcalBundle1962 => b != null
|
||||||
|
);
|
||||||
|
for (const b of bundles) {
|
||||||
|
const names = b.i18n.names;
|
||||||
|
const v = names?.[key] ?? names?.[id];
|
||||||
|
if (v && v !== key && v !== id) return v;
|
||||||
|
}
|
||||||
|
return humanizeId(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function adaptDay1962(
|
||||||
|
d: LiturgicalDay1962,
|
||||||
|
lang: CalendarLang,
|
||||||
|
laBundle: RomcalBundle1962,
|
||||||
|
localBundle: RomcalBundle1962 | null
|
||||||
|
): CalendarDay {
|
||||||
|
const colorKeys = colorKeysFrom(d);
|
||||||
|
const colorNames = colorKeys.map((k) => colorLabel1962(k, lang));
|
||||||
|
const classOf = (d.classOf1962 ?? 4) as 1 | 2 | 3 | 4;
|
||||||
|
const classKey = RANK_FROM_CLASS_1962[classOf];
|
||||||
|
|
||||||
|
const laProps = findPropersFor(d, laBundle);
|
||||||
|
const localProps = localBundle ? findPropersFor(d, localBundle) : undefined;
|
||||||
|
const propers = sectionsFromBundle(laProps, localProps);
|
||||||
|
|
||||||
|
const detail: Rite1962Detail = {
|
||||||
|
class: classOf,
|
||||||
|
kind: d.kind1962 ?? 'tempora',
|
||||||
|
commemorations: buildCommemorations(d),
|
||||||
|
propers
|
||||||
|
};
|
||||||
|
if (d.octaveOf) detail.octave = { ofId: d.octaveOf.ofId, day: d.octaveOf.day };
|
||||||
|
if (d.vigilOf) detail.vigilOf = d.vigilOf;
|
||||||
|
if (d.transferredFromDate) detail.transferredFrom = d.transferredFromDate;
|
||||||
|
|
||||||
|
const seasonKey = d.seasons?.[0] ?? null;
|
||||||
return {
|
return {
|
||||||
iso: p.date,
|
iso: d.date,
|
||||||
id: p.key,
|
id: d.id,
|
||||||
name: p.name,
|
name: resolveName1962(d, localBundle, laBundle),
|
||||||
rankName: rank1962Label(p.rank1962, lang),
|
rankName: rank1962Label(classKey, lang),
|
||||||
rank: RANK_FROM_CLASS_1962[p.classOf1962],
|
rank: classKey,
|
||||||
seasonKey,
|
seasonKey,
|
||||||
seasonNames,
|
seasonNames: d.seasonNames ? [...d.seasonNames] : [],
|
||||||
colorNames,
|
colorNames,
|
||||||
colorKeys,
|
colorKeys,
|
||||||
psalterWeek: null,
|
psalterWeek: null,
|
||||||
@@ -430,13 +299,23 @@ export async function getYear1962(
|
|||||||
const cacheKey = `${diocese}|${lang}|${year}`;
|
const cacheKey = `${diocese}|${lang}|${year}`;
|
||||||
const cached = yearCache1962.get(cacheKey);
|
const cached = yearCache1962.get(cacheKey);
|
||||||
if (cached) return cached;
|
if (cached) return cached;
|
||||||
const resolved = await getRomcal1962(lang, diocese).generateCalendar(year);
|
|
||||||
|
const [romcal, laBundle, localBundle] = await Promise.all([
|
||||||
|
getRomcal1962(lang, diocese),
|
||||||
|
loadBundle1962('la'),
|
||||||
|
lang === 'la' ? Promise.resolve<RomcalBundle1962 | null>(null) : loadBundle1962(lang)
|
||||||
|
]);
|
||||||
|
const resolved = await romcal.generateCalendar(year);
|
||||||
const map = new Map<string, CalendarDay>();
|
const map = new Map<string, CalendarDay>();
|
||||||
for (const [iso, entries] of Object.entries(resolved)) {
|
for (const [iso, entries] of Object.entries(resolved)) {
|
||||||
if (!entries.length) continue;
|
const principal = entries[0];
|
||||||
map.set(iso, adaptDay1962(entries, lang));
|
if (!principal) continue;
|
||||||
|
map.set(iso, adaptDay1962(principal, lang, laBundle, localBundle));
|
||||||
}
|
}
|
||||||
yearCache1962.set(cacheKey, map);
|
yearCache1962.set(cacheKey, map);
|
||||||
|
// keep season1962Label referenced so tree-shakers don't drop it while the
|
||||||
|
// detail view gradually takes over rendering its own labels.
|
||||||
|
void season1962Label;
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-1
@@ -23,7 +23,6 @@ import type { CalendarDay, SeasonArc, YearDay } from '$lib/calendarTypes';
|
|||||||
export type {
|
export type {
|
||||||
CalendarDay,
|
CalendarDay,
|
||||||
ProperSection,
|
ProperSection,
|
||||||
ProperSegment,
|
|
||||||
Rite1962Commem,
|
Rite1962Commem,
|
||||||
Rite1962Detail,
|
Rite1962Detail,
|
||||||
SeasonArc,
|
SeasonArc,
|
||||||
|
|||||||
+44
-75
@@ -238,22 +238,11 @@
|
|||||||
<span class="tc-tag">{t('cycle', lang)}: {humanizeSundayCycle(hero.sundayCycle)}</span>
|
<span class="tc-tag">{t('cycle', lang)}: {humanizeSundayCycle(hero.sundayCycle)}</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if hero.rite1962}
|
{#if hero.rite1962 && hero.rite1962.commemorations.length}
|
||||||
{@const r = hero.rite1962.rubrics}
|
<div class="tc-commems">
|
||||||
<div class="tc-rubrics">
|
{#each hero.rite1962.commemorations as c (c.id)}
|
||||||
<span class="tc-rubric" class:on={r.gloria}>
|
<span class="tc-commem">{c.name}</span>
|
||||||
<span class="tc-rubric-dot"></span>
|
{/each}
|
||||||
<b>{t1962('gloria', lang)}</b>
|
|
||||||
<span class="tc-state">{r.gloria ? t1962('yes', lang) : t1962('no', lang)}</span>
|
|
||||||
</span>
|
|
||||||
<span class="tc-rubric" class:on={r.credo}>
|
|
||||||
<span class="tc-rubric-dot"></span>
|
|
||||||
<b>{t1962('credo', lang)}</b>
|
|
||||||
<span class="tc-state">{r.credo ? t1962('yes', lang) : t1962('no', lang)}</span>
|
|
||||||
</span>
|
|
||||||
{#if r.preface}
|
|
||||||
<span class="tc-preface"><em>{t1962('preface', lang)}:</em> {r.preface}</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
<span class="tc-arrow" aria-hidden="true">→</span>
|
<span class="tc-arrow" aria-hidden="true">→</span>
|
||||||
@@ -281,20 +270,31 @@
|
|||||||
▦ {lang === 'de' ? 'Monat' : lang === 'la' ? 'Mensis' : 'Month'}
|
▦ {lang === 'de' ? 'Monat' : lang === 'la' ? 'Mensis' : 'Month'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="overview-right">
|
||||||
<div class="legend" aria-hidden={!Object.keys(LIT_COLOR_VAR).length}>
|
<div class="legend" aria-hidden={!Object.keys(LIT_COLOR_VAR).length}>
|
||||||
{#each Object.keys(LIT_COLOR_VAR) as key (key)}
|
{#each Object.keys(LIT_COLOR_VAR) as key (key)}
|
||||||
{@const label =
|
{@const label =
|
||||||
lang === 'de'
|
lang === 'de'
|
||||||
? { WHITE: 'Weiß', RED: 'Rot', GREEN: 'Grün', PURPLE: 'Violett', ROSE: 'Rosa', BLACK: 'Schwarz', GOLD: 'Gold' }[key]
|
? { WHITE: 'Weiß', RED: 'Rot', GREEN: 'Grün', PURPLE: 'Violett', ROSE: 'Rosa', BLACK: 'Schwarz' }[key]
|
||||||
: lang === 'la'
|
: lang === 'la'
|
||||||
? { WHITE: 'Albus', RED: 'Ruber', GREEN: 'Viridis', PURPLE: 'Violaceus', ROSE: 'Rosaceus', BLACK: 'Niger', GOLD: 'Aureus' }[key]
|
? { WHITE: 'Albus', RED: 'Ruber', GREEN: 'Viridis', PURPLE: 'Violaceus', ROSE: 'Rosaceus', BLACK: 'Niger' }[key]
|
||||||
: { WHITE: 'White', RED: 'Red', GREEN: 'Green', PURPLE: 'Violet', ROSE: 'Rose', BLACK: 'Black', GOLD: 'Gold' }[key]}
|
: { WHITE: 'White', RED: 'Red', GREEN: 'Green', PURPLE: 'Violet', ROSE: 'Rose', BLACK: 'Black' }[key]}
|
||||||
<span class="swatch">
|
<span class="swatch">
|
||||||
<span class="sq" style="background: {litBg(key)}"></span>
|
<span class="sq" style="background: {litBg(key)}"></span>
|
||||||
<span>{label}</span>
|
<span>{label}</span>
|
||||||
</span>
|
</span>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
<a
|
||||||
|
class="jump-btn jump-btn-gold"
|
||||||
|
href={todayHref}
|
||||||
|
data-sveltekit-noscroll
|
||||||
|
data-sveltekit-replacestate
|
||||||
|
>
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
||||||
|
{t('jumpToToday', lang)}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if view === 'ring'}
|
{#if view === 'ring'}
|
||||||
@@ -333,13 +333,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="today-jump-row">
|
|
||||||
<a class="jump-btn" href={todayHref} data-sveltekit-noscroll>
|
|
||||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
|
|
||||||
{t('jumpToToday', lang)}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<div class="grid-header" role="row">
|
<div class="grid-header" role="row">
|
||||||
{#each weekdayLabels as wd (wd)}
|
{#each weekdayLabels as wd (wd)}
|
||||||
@@ -642,60 +635,21 @@
|
|||||||
letter-spacing: 0.06em;
|
letter-spacing: 0.06em;
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
.tc-rubrics {
|
.tc-commems {
|
||||||
margin-top: 1.4rem;
|
margin-top: 1.4rem;
|
||||||
padding-top: 1.1rem;
|
padding-top: 1.1rem;
|
||||||
border-top: 1px solid rgba(255, 255, 255, 0.22);
|
border-top: 1px solid rgba(255, 255, 255, 0.22);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 0.5rem 1.1rem;
|
gap: 0.4rem 0.6rem;
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
.tc-rubric {
|
.tc-commem {
|
||||||
display: inline-flex;
|
padding: 0.3rem 0.7rem;
|
||||||
align-items: center;
|
|
||||||
gap: 0.45rem;
|
|
||||||
padding: 0.35rem 0.75rem 0.35rem 0.6rem;
|
|
||||||
border-radius: var(--radius-pill);
|
border-radius: var(--radius-pill);
|
||||||
background: rgba(255, 255, 255, 0.18);
|
background: rgba(255, 255, 255, 0.18);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.22);
|
border: 1px solid rgba(255, 255, 255, 0.22);
|
||||||
font-size: 0.82rem;
|
font-size: 0.82rem;
|
||||||
}
|
}
|
||||||
.tc-rubric b {
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
.tc-rubric .tc-state {
|
|
||||||
font-size: 0.72rem;
|
|
||||||
opacity: 0.72;
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
.tc-rubric-dot {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: rgba(255, 255, 255, 0.55);
|
|
||||||
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.15);
|
|
||||||
}
|
|
||||||
.tc-rubric.on .tc-rubric-dot {
|
|
||||||
background: #8be78b;
|
|
||||||
box-shadow: 0 0 0 2px rgba(139, 231, 139, 0.22);
|
|
||||||
}
|
|
||||||
.tc-rubric:not(.on) b {
|
|
||||||
opacity: 0.65;
|
|
||||||
}
|
|
||||||
.tc-preface {
|
|
||||||
font-size: 0.85rem;
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
.tc-preface em {
|
|
||||||
font-style: normal;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 0.68rem;
|
|
||||||
letter-spacing: 0.1em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
margin-right: 0.4rem;
|
|
||||||
opacity: 0.72;
|
|
||||||
}
|
|
||||||
.tc-arrow {
|
.tc-arrow {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 1.1rem;
|
bottom: 1.1rem;
|
||||||
@@ -760,10 +714,17 @@
|
|||||||
.view-switcher button:active {
|
.view-switcher button:active {
|
||||||
transform: scale(0.95);
|
transform: scale(0.95);
|
||||||
}
|
}
|
||||||
|
.overview-right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
.legend {
|
.legend {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 0.4rem;
|
gap: 0.4rem;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
.legend .swatch {
|
.legend .swatch {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@@ -837,11 +798,6 @@
|
|||||||
box-shadow: var(--shadow-hover);
|
box-shadow: var(--shadow-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
.today-jump-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
}
|
|
||||||
.jump-btn {
|
.jump-btn {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -851,12 +807,25 @@
|
|||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
border-radius: var(--radius-pill);
|
border-radius: var(--radius-pill);
|
||||||
font-size: var(--text-sm);
|
font-size: var(--text-sm);
|
||||||
transition: background var(--transition-fast), transform var(--transition-fast);
|
box-shadow: var(--shadow-sm);
|
||||||
|
transition: background var(--transition-fast), transform var(--transition-fast),
|
||||||
|
box-shadow var(--transition-fast);
|
||||||
}
|
}
|
||||||
.jump-btn:hover {
|
.jump-btn:hover {
|
||||||
background: var(--color-bg-elevated);
|
background: var(--color-bg-elevated);
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
transform: scale(1.03);
|
transform: scale(1.03);
|
||||||
|
box-shadow: var(--shadow-hover);
|
||||||
|
}
|
||||||
|
.jump-btn-gold {
|
||||||
|
background: var(--lit-gold);
|
||||||
|
color: var(--lit-gold-ink);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
.jump-btn-gold:hover {
|
||||||
|
background: var(--lit-gold);
|
||||||
|
color: var(--lit-gold-ink);
|
||||||
|
filter: brightness(1.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid {
|
.grid {
|
||||||
|
|||||||
+21
-132
@@ -12,6 +12,12 @@
|
|||||||
t1962,
|
t1962,
|
||||||
type CalendarLang
|
type CalendarLang
|
||||||
} from '../../../../../calendarI18n';
|
} from '../../../../../calendarI18n';
|
||||||
|
|
||||||
|
function kindLabel(kind: 'tempora' | 'sancti', l: CalendarLang): string {
|
||||||
|
if (kind === 'tempora')
|
||||||
|
return l === 'de' ? 'Temporale' : l === 'la' ? 'Temporale' : 'Temporal';
|
||||||
|
return l === 'de' ? 'Sanktorale' : l === 'la' ? 'Sanctorale' : 'Sanctoral';
|
||||||
|
}
|
||||||
import { litBg, litInk } from '../../../../../calendarColors';
|
import { litBg, litInk } from '../../../../../calendarColors';
|
||||||
|
|
||||||
let { data }: { data: PageData } = $props();
|
let { data }: { data: PageData } = $props();
|
||||||
@@ -115,7 +121,7 @@
|
|||||||
<dl class="detail-extras">
|
<dl class="detail-extras">
|
||||||
<div>
|
<div>
|
||||||
<dt>{t1962('source', lang)}</dt>
|
<dt>{t1962('source', lang)}</dt>
|
||||||
<dd>{d.kind}{d.properSource ? ` · ${d.properSource}` : ''}{d.communeSlug ? ` (${d.communeSlug})` : ''}</dd>
|
<dd>{kindLabel(d.kind, lang)}</dd>
|
||||||
</div>
|
</div>
|
||||||
{#if d.vigilOf}
|
{#if d.vigilOf}
|
||||||
<div>
|
<div>
|
||||||
@@ -126,7 +132,7 @@
|
|||||||
{#if d.octave}
|
{#if d.octave}
|
||||||
<div>
|
<div>
|
||||||
<dt>{t1962('octave', lang)}</dt>
|
<dt>{t1962('octave', lang)}</dt>
|
||||||
<dd>{d.octave.id} · {t1962('octaveDay', lang)} {d.octave.day} · {d.octave.rank}</dd>
|
<dd>{d.octave.ofId} · {t1962('octaveDay', lang)} {d.octave.day}</dd>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if d.transferredFrom}
|
{#if d.transferredFrom}
|
||||||
@@ -136,32 +142,13 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</dl>
|
</dl>
|
||||||
<div class="rubrics-grid">
|
|
||||||
<h4>{t1962('rubrics', lang)}</h4>
|
|
||||||
<div class="rubric-row">
|
|
||||||
<span class="rubric-chip" class:on={d.rubrics.gloria}>{t1962('gloria', lang)}: {d.rubrics.gloria ? t1962('yes', lang) : t1962('no', lang)}</span>
|
|
||||||
<span class="rubric-chip" class:on={d.rubrics.credo}>{t1962('credo', lang)}: {d.rubrics.credo ? t1962('yes', lang) : t1962('no', lang)}</span>
|
|
||||||
{#if d.rubrics.preface}
|
|
||||||
<span class="rubric-chip on">{t1962('preface', lang)}: {d.rubrics.preface}</span>
|
|
||||||
{/if}
|
|
||||||
{#if d.rubrics.lastGospel}
|
|
||||||
<span class="rubric-chip on">{t1962('lastGospel', lang)}: {d.rubrics.lastGospel}</span>
|
|
||||||
{/if}
|
|
||||||
{#if d.rubrics.ite}
|
|
||||||
<span class="rubric-chip on">{t1962('ite', lang)}: {d.rubrics.ite}</span>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{#if d.commemorations.length}
|
{#if d.commemorations.length}
|
||||||
<div class="commems">
|
<div class="commems">
|
||||||
<h4>{t1962('commemorations', lang)}</h4>
|
<h4>{t1962('commemorations', lang)}</h4>
|
||||||
<ul>
|
<ul>
|
||||||
{#each d.commemorations as c (c.key)}
|
{#each d.commemorations as c (c.id)}
|
||||||
{@const cHex = hexFor(c.colorKeys)}
|
|
||||||
<li>
|
<li>
|
||||||
<span class="color-swatch" style="background: {cHex}"></span>
|
|
||||||
<span class="commem-name">{c.name}</span>
|
<span class="commem-name">{c.name}</span>
|
||||||
<span class="commem-rank">{c.rankName}</span>
|
|
||||||
</li>
|
</li>
|
||||||
{/each}
|
{/each}
|
||||||
</ul>
|
</ul>
|
||||||
@@ -171,62 +158,26 @@
|
|||||||
<section class="propers">
|
<section class="propers">
|
||||||
<h4>{t1962('propers', lang)}</h4>
|
<h4>{t1962('propers', lang)}</h4>
|
||||||
{#each d.propers as section (section.key)}
|
{#each d.propers as section (section.key)}
|
||||||
|
{@const rows = Math.max(section.la.length, section.local.length)}
|
||||||
<div class="proper-block">
|
<div class="proper-block">
|
||||||
<div class="proper-label-row">
|
<div class="proper-label-row">
|
||||||
<span class="proper-label">{properLabel(section.key, lang)}</span>
|
<span class="proper-label">{properLabel(section.key, lang)}</span>
|
||||||
</div>
|
</div>
|
||||||
{#each section.segments as seg, segIdx (segIdx)}
|
{#each Array(rows) as _, i (i)}
|
||||||
|
{@const la = section.la[i] ?? ''}
|
||||||
|
{@const local = section.local[i] ?? ''}
|
||||||
|
{#if la || local}
|
||||||
<div class="proper-segment">
|
<div class="proper-segment">
|
||||||
{#if seg.refs && seg.refs.length}
|
<div class="proper-cols" class:single={lang === 'la' || !local}>
|
||||||
<div class="proper-segment-refs">
|
{#if la}
|
||||||
{#each seg.refs as r (r)}
|
<div class="proper-col proper-col-la" lang="la">{la}</div>
|
||||||
<span class="proper-ref">{r}</span>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
{#if seg.la || seg.local}
|
{#if lang !== 'la' && local}
|
||||||
<div class="proper-cols" class:single={lang === 'la' || !seg.local}>
|
<div class="proper-col proper-col-local" lang={lang}>{local}</div>
|
||||||
{#if lang !== 'la' && seg.local && seg.fromBible}
|
|
||||||
<p class="proper-fallback-note">{t1962('bibleFallbackNote', lang)}</p>
|
|
||||||
{/if}
|
|
||||||
{#if seg.la}
|
|
||||||
<div class="proper-col proper-col-la" lang="la">{seg.la}</div>
|
|
||||||
{/if}
|
|
||||||
{#if lang !== 'la' && seg.local}
|
|
||||||
<div class="proper-col proper-col-local" lang={lang}>{seg.local}</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</section>
|
|
||||||
{/if}
|
|
||||||
{#if d.extraSections.length}
|
|
||||||
<section class="propers">
|
|
||||||
<h4>{t1962('extraSections', lang)}</h4>
|
|
||||||
{#each d.extraSections as section (section.key)}
|
|
||||||
<div class="proper-block">
|
|
||||||
<div class="proper-label-row">
|
|
||||||
<span class="proper-label">{properLabel(section.key, lang)}</span>
|
|
||||||
</div>
|
|
||||||
{#each section.segments as seg, segIdx (segIdx)}
|
|
||||||
<div class="proper-segment">
|
|
||||||
{#if seg.refs && seg.refs.length}
|
|
||||||
<div class="proper-segment-refs">
|
|
||||||
{#each seg.refs as r (r)}
|
|
||||||
<span class="proper-ref">{r}</span>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
<div class="proper-cols" class:single={lang === 'la' || !seg.local}>
|
|
||||||
<div class="proper-col proper-col-la" lang="la">{seg.la}</div>
|
|
||||||
{#if lang !== 'la' && seg.local}
|
|
||||||
<div class="proper-col proper-col-local" lang={lang}>{seg.local}</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
@@ -399,10 +350,6 @@
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
}
|
}
|
||||||
.rubrics-grid {
|
|
||||||
margin-top: 0.75rem;
|
|
||||||
}
|
|
||||||
.rubrics-grid h4,
|
|
||||||
.commems h4,
|
.commems h4,
|
||||||
.propers h4 {
|
.propers h4 {
|
||||||
margin: 0.5rem 0 0.4rem;
|
margin: 0.5rem 0 0.4rem;
|
||||||
@@ -412,24 +359,6 @@
|
|||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
.rubric-row {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 0.4rem;
|
|
||||||
}
|
|
||||||
.rubric-chip {
|
|
||||||
padding: 0.25rem 0.6rem;
|
|
||||||
border-radius: var(--radius-pill);
|
|
||||||
background: var(--color-bg-tertiary);
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
font-size: 0.78rem;
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
.rubric-chip.on {
|
|
||||||
background: color-mix(in srgb, var(--accent) 15%, var(--color-bg-tertiary));
|
|
||||||
color: var(--color-text-primary);
|
|
||||||
border-color: color-mix(in srgb, var(--accent) 30%, var(--color-border));
|
|
||||||
}
|
|
||||||
.commems {
|
.commems {
|
||||||
margin-top: 0.75rem;
|
margin-top: 0.75rem;
|
||||||
}
|
}
|
||||||
@@ -450,18 +379,10 @@
|
|||||||
border-radius: var(--radius-sm, 6px);
|
border-radius: var(--radius-sm, 6px);
|
||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
}
|
}
|
||||||
.commems .color-swatch {
|
|
||||||
border-color: var(--color-border);
|
|
||||||
}
|
|
||||||
.commem-name {
|
.commem-name {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
}
|
}
|
||||||
.commem-rank {
|
|
||||||
font-size: 0.72rem;
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.propers {
|
.propers {
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
@@ -485,15 +406,6 @@
|
|||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
letter-spacing: 0.03em;
|
letter-spacing: 0.03em;
|
||||||
}
|
}
|
||||||
.proper-ref {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0.1rem 0.5rem;
|
|
||||||
border-radius: var(--radius-pill);
|
|
||||||
font-size: 0.72rem;
|
|
||||||
background: var(--color-bg-tertiary);
|
|
||||||
color: var(--color-text-primary);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
}
|
|
||||||
.proper-segment {
|
.proper-segment {
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
}
|
}
|
||||||
@@ -504,24 +416,6 @@
|
|||||||
padding-top: 0.5rem;
|
padding-top: 0.5rem;
|
||||||
border-top: 1px dashed var(--color-border);
|
border-top: 1px dashed var(--color-border);
|
||||||
}
|
}
|
||||||
.proper-segment-refs {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 0.35rem;
|
|
||||||
margin-bottom: 0.35rem;
|
|
||||||
}
|
|
||||||
.proper-fallback-note {
|
|
||||||
grid-column: 2 / 3;
|
|
||||||
grid-row: 1;
|
|
||||||
margin: 0 0 0.25rem;
|
|
||||||
padding: 0.4rem 0.6rem;
|
|
||||||
border-left: 2px solid color-mix(in srgb, var(--orange) 55%, transparent);
|
|
||||||
background: color-mix(in srgb, var(--orange) 8%, transparent);
|
|
||||||
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
|
|
||||||
font-size: 0.78rem;
|
|
||||||
line-height: 1.4;
|
|
||||||
color: var(--color-text-secondary);
|
|
||||||
}
|
|
||||||
.proper-cols {
|
.proper-cols {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
@@ -531,12 +425,10 @@
|
|||||||
}
|
}
|
||||||
.proper-col-la {
|
.proper-col-la {
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
grid-row: 2;
|
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
.proper-col-local {
|
.proper-col-local {
|
||||||
grid-column: 2;
|
grid-column: 2;
|
||||||
grid-row: 2;
|
|
||||||
}
|
}
|
||||||
.proper-cols.single {
|
.proper-cols.single {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
@@ -544,7 +436,6 @@
|
|||||||
.proper-cols.single .proper-col-la,
|
.proper-cols.single .proper-col-la,
|
||||||
.proper-cols.single .proper-col-local {
|
.proper-cols.single .proper-col-local {
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
grid-row: auto;
|
|
||||||
}
|
}
|
||||||
.proper-col {
|
.proper-col {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
@@ -564,10 +455,8 @@
|
|||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
.proper-cols .proper-col-la,
|
.proper-cols .proper-col-la,
|
||||||
.proper-cols .proper-col-local,
|
.proper-cols .proper-col-local {
|
||||||
.proper-cols .proper-fallback-note {
|
|
||||||
grid-column: 1;
|
grid-column: 1;
|
||||||
grid-row: auto;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media (max-width: 560px) {
|
@media (max-width: 560px) {
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
// Liturgical color tokens used by the overview design.
|
// Liturgical color tokens used by the overview design.
|
||||||
// Maps romcal color keys to the CSS variables defined on the calendar page.
|
// Maps romcal color keys to the CSS variables defined on the calendar page.
|
||||||
|
|
||||||
|
// Romcal never emits GOLD for either rite, so it is excluded from the legend
|
||||||
|
// and lookup here. The --lit-gold token still exists for today-pin styling.
|
||||||
export const LIT_COLOR_VAR: Record<string, string> = {
|
export const LIT_COLOR_VAR: Record<string, string> = {
|
||||||
WHITE: '--lit-white',
|
WHITE: '--lit-white',
|
||||||
RED: '--lit-red',
|
RED: '--lit-red',
|
||||||
GREEN: '--lit-green',
|
GREEN: '--lit-green',
|
||||||
PURPLE: '--lit-violet',
|
PURPLE: '--lit-violet',
|
||||||
ROSE: '--lit-rose',
|
ROSE: '--lit-rose',
|
||||||
BLACK: '--lit-black',
|
BLACK: '--lit-black'
|
||||||
GOLD: '--lit-gold'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LIT_INK_VAR: Record<string, string> = {
|
export const LIT_INK_VAR: Record<string, string> = {
|
||||||
@@ -17,8 +18,7 @@ export const LIT_INK_VAR: Record<string, string> = {
|
|||||||
GREEN: '--lit-green-ink',
|
GREEN: '--lit-green-ink',
|
||||||
PURPLE: '--lit-violet-ink',
|
PURPLE: '--lit-violet-ink',
|
||||||
ROSE: '--lit-rose-ink',
|
ROSE: '--lit-rose-ink',
|
||||||
BLACK: '--lit-black-ink',
|
BLACK: '--lit-black-ink'
|
||||||
GOLD: '--lit-gold-ink'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function litBg(colorKey: string | undefined): string {
|
export function litBg(colorKey: string | undefined): string {
|
||||||
|
|||||||
@@ -59,11 +59,18 @@ export function hexFor(colorKeys: string[]): string {
|
|||||||
return colorHex[first] ?? '#27AE60';
|
return colorHex[first] ?? '#27AE60';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rank emphasis for visual weighting of cells
|
// Rank emphasis for visual weighting of cells. Accepts both 1969 rank keys and
|
||||||
|
// 1962 class labels (ClassI..IV), since 1962 days are emphasized similarly.
|
||||||
export function rankEmphasis(rank: string): number {
|
export function rankEmphasis(rank: string): number {
|
||||||
if (rank === 'SOLEMNITY') return 3;
|
if (rank === 'SOLEMNITY' || rank === 'ClassI') return 3;
|
||||||
if (rank === 'FEAST' || rank === 'SUNDAY' || rank === 'HOLY_DAY_OF_OBLIGATION') return 2;
|
if (
|
||||||
if (rank === 'MEMORIAL') return 1;
|
rank === 'FEAST' ||
|
||||||
|
rank === 'SUNDAY' ||
|
||||||
|
rank === 'HOLY_DAY_OF_OBLIGATION' ||
|
||||||
|
rank === 'ClassII'
|
||||||
|
)
|
||||||
|
return 2;
|
||||||
|
if (rank === 'MEMORIAL' || rank === 'ClassIII') return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +252,14 @@ const SEASON_LABEL: Record<string, Record<CalendarLang, string>> = {
|
|||||||
EasterWeek: { en: 'Easter Week', de: 'Osteroktav', la: 'Hebdomada Paschae' },
|
EasterWeek: { en: 'Easter Week', de: 'Osteroktav', la: 'Hebdomada Paschae' },
|
||||||
Paschaltide: { en: 'Eastertide', de: 'Osterzeit', la: 'Tempus Paschale' },
|
Paschaltide: { en: 'Eastertide', de: 'Osterzeit', la: 'Tempus Paschale' },
|
||||||
Pentecost: { en: 'Pentecost Week', de: 'Pfingstoktav', la: 'Hebdomada Pentecostes' },
|
Pentecost: { en: 'Pentecost Week', de: 'Pfingstoktav', la: 'Hebdomada Pentecostes' },
|
||||||
TimeAfterPentecost: { en: 'after Pentecost', de: 'nach Pfingsten', la: 'post Pentecosten' }
|
TimeAfterPentecost: { en: 'after Pentecost', de: 'nach Pfingsten', la: 'post Pentecosten' },
|
||||||
|
// romcal 3 emits 1969-style SCREAMING_SNAKE season keys even for the 1962 calendar.
|
||||||
|
ADVENT: { en: 'Advent', de: 'Advent', la: 'Adventus' },
|
||||||
|
CHRISTMAS_TIME: { en: 'Christmastide', de: 'Weihnachtszeit', la: 'Tempus Nativitatis' },
|
||||||
|
LENT: { en: 'Lent', de: 'Fastenzeit', la: 'Quadragesima' },
|
||||||
|
PASCHAL_TRIDUUM: { en: 'Paschal Triduum', de: 'Ostertriduum', la: 'Triduum Paschale' },
|
||||||
|
EASTER_TIME: { en: 'Eastertide', de: 'Osterzeit', la: 'Tempus Paschale' },
|
||||||
|
ORDINARY_TIME: { en: 'after Pentecost', de: 'nach Pfingsten', la: 'post Pentecosten' }
|
||||||
};
|
};
|
||||||
|
|
||||||
export function season1962Label(season: string, lang: CalendarLang): string {
|
export function season1962Label(season: string, lang: CalendarLang): string {
|
||||||
@@ -268,26 +282,12 @@ export function colorLabel1962(colorKey: string, lang: CalendarLang): string {
|
|||||||
|
|
||||||
export const ui1962 = {
|
export const ui1962 = {
|
||||||
commemorations: { en: 'Commemorations', de: 'Kommemorationen', la: 'Commemorationes' },
|
commemorations: { en: 'Commemorations', de: 'Kommemorationen', la: 'Commemorationes' },
|
||||||
rubrics: { en: 'Rubrics', de: 'Rubriken', la: 'Rubricæ' },
|
|
||||||
gloria: { en: 'Gloria', de: 'Gloria', la: 'Gloria' },
|
|
||||||
credo: { en: 'Credo', de: 'Credo', la: 'Credo' },
|
|
||||||
preface: { en: 'Preface', de: 'Präfation', la: 'Præfatio' },
|
|
||||||
lastGospel: { en: 'Last Gospel', de: 'Letztes Evangelium', la: 'Ultimum Evangelium' },
|
|
||||||
ite: { en: 'Dismissal', de: 'Entlassung', la: 'Dimissio' },
|
|
||||||
octave: { en: 'Octave', de: 'Oktav', la: 'Octava' },
|
octave: { en: 'Octave', de: 'Oktav', la: 'Octava' },
|
||||||
octaveDay: { en: 'day', de: 'Tag', la: 'dies' },
|
octaveDay: { en: 'day', de: 'Tag', la: 'dies' },
|
||||||
vigilOf: { en: 'Vigil of', de: 'Vigil von', la: 'Vigilia' },
|
vigilOf: { en: 'Vigil of', de: 'Vigil von', la: 'Vigilia' },
|
||||||
transferredFrom: { en: 'Transferred from', de: 'Übertragen von', la: 'Translatum ex' },
|
transferredFrom: { en: 'Transferred from', de: 'Übertragen von', la: 'Translatum ex' },
|
||||||
source: { en: 'Source', de: 'Quelle', la: 'Fons' },
|
source: { en: 'Source', de: 'Quelle', la: 'Fons' },
|
||||||
yes: { en: 'yes', de: 'ja', la: 'sic' },
|
propers: { en: 'Mass propers', de: 'Messproprium', la: 'Propria Missæ' }
|
||||||
no: { en: 'no', de: 'nein', la: 'non' },
|
|
||||||
properRef: { en: 'Proper', de: 'Proprium', la: 'Proprium' },
|
|
||||||
propers: { en: 'Mass propers', de: 'Messproprium', la: 'Propria Missæ' },
|
|
||||||
extraSections: { en: 'Additional readings', de: 'Zusätzliche Lesungen', la: 'Lectiones additae' },
|
|
||||||
bibleFallbackNote: {
|
|
||||||
en: 'Translation taken from the Douay-Rheims Bible, since no translated proper is published for this section. Wording will differ from authoritative missals.',
|
|
||||||
de: 'Übersetzung stammt aus der Allioli-Bibel, da für diesen Abschnitt kein übersetztes Proprium veröffentlicht ist. Wortlaut weicht von maßgeblichen Messbüchern ab.'
|
|
||||||
}
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export function t1962(key: keyof typeof ui1962, lang: CalendarLang): string {
|
export function t1962(key: keyof typeof ui1962, lang: CalendarLang): string {
|
||||||
@@ -296,19 +296,18 @@ export function t1962(key: keyof typeof ui1962, lang: CalendarLang): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const PROPER_LABEL: Record<string, Record<CalendarLang, string>> = {
|
const PROPER_LABEL: Record<string, Record<CalendarLang, string>> = {
|
||||||
introit: { en: 'Introit', de: 'Introitus', la: 'Introitus' },
|
Introitus: { en: 'Introit', de: 'Introitus', la: 'Introitus' },
|
||||||
collect: { en: 'Collect', de: 'Kollekte', la: 'Collecta' },
|
Oratio: { en: 'Collect', de: 'Kollekte', la: 'Oratio' },
|
||||||
epistle: { en: 'Epistle', de: 'Epistel', la: 'Epistola' },
|
Lectio: { en: 'Epistle', de: 'Epistel', la: 'Lectio' },
|
||||||
gradual: { en: 'Gradual', de: 'Graduale', la: 'Graduale' },
|
Graduale: { en: 'Gradual', de: 'Graduale', la: 'Graduale' },
|
||||||
alleluia: { en: 'Alleluia', de: 'Alleluja', la: 'Alleluia' },
|
GradualeF: { en: 'Alleluia', de: 'Alleluja', la: 'Alleluia' },
|
||||||
tract: { en: 'Tract', de: 'Tractus', la: 'Tractus' },
|
Tractus: { en: 'Tract', de: 'Tractus', la: 'Tractus' },
|
||||||
sequence: { en: 'Sequence', de: 'Sequenz', la: 'Sequentia' },
|
Sequentia: { en: 'Sequence', de: 'Sequenz', la: 'Sequentia' },
|
||||||
gospel: { en: 'Gospel', de: 'Evangelium', la: 'Evangelium' },
|
Evangelium: { en: 'Gospel', de: 'Evangelium', la: 'Evangelium' },
|
||||||
offertory: { en: 'Offertory', de: 'Offertorium', la: 'Offertorium' },
|
Offertorium: { en: 'Offertory', de: 'Offertorium', la: 'Offertorium' },
|
||||||
secret: { en: 'Secret', de: 'Stillgebet', la: 'Secreta' },
|
Secreta: { en: 'Secret', de: 'Stillgebet', la: 'Secreta' },
|
||||||
preface: { en: 'Preface', de: 'Präfation', la: 'Præfatio' },
|
Communio: { en: 'Communion', de: 'Kommunion', la: 'Communio' },
|
||||||
communion: { en: 'Communion', de: 'Kommunion', la: 'Communio' },
|
Postcommunio: { en: 'Postcommunion', de: 'Schlussgebet', la: 'Postcommunio' }
|
||||||
postcommunion: { en: 'Postcommunion', de: 'Schlussgebet', la: 'Postcommunio' }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function properLabel(key: string, lang: CalendarLang): string {
|
export function properLabel(key: string, lang: CalendarLang): string {
|
||||||
|
|||||||
Reference in New Issue
Block a user