Files
church-translate/app/liturgy.js
T

119 lines
5.6 KiB
JavaScript
Raw Permalink Normal View History

/*
* liturgy.js — the handful of words that must never be guessed at.
*
* Measured against the shipped NLLB, the single most frequent word in a service
* translates as follows:
*
* "Amen." → spa "- Sí, estoy bien." ("yes, I'm fine")
* fra "Je vous en prie." ("you're welcome")
* deu "- Das ist doch gut." ("well, that's good")
* zho "现在,我们要去." ("now, we're going to go")
* rus "- Да, да." ("yes, yes")
*
* Of ten languages tested, four were right. This is not a translation problem
* that better decoding fixes — one word carries no context for a context model
* to work from. So these bypass the model completely.
*
* SCOPE IS DELIBERATELY TINY. Only Hebrew LOANWORDS are here — amen, hallelujah,
* hosanna — words every tradition transliterated rather than translated, so
* there is one right answer and no denominational choice hiding in it.
* "Praise the Lord" and "Glory to God" are NOT here: they are translated
* phrases, and their wording differs between Protestant and Catholic editions
* of the same language. Guessing those would be the exact silent, confident
* error this file exists to prevent.
*
* Matching is WHOLE-UTTERANCE ONLY. "Amen" is a title of Christ in Revelation
* 3:14 and a deity in an Exodus sermon; "can I get an amen?" is a sentence.
* None of those may be touched, and equality matching means none of them are.
*/
(function (root, factory) {
if (typeof module === 'object' && module.exports) module.exports = factory();
else root.Liturgy = factory();
}(typeof self !== 'undefined' ? self : this, function () {
'use strict';
/*
* Whisper writes these many ways: "Amen", "amen!", "A-men.", "Amen...".
*
* Hyphens are removed (A-men -> amen) but whitespace is COLLAPSED, not
* deleted. Deleting it turned "praise god" into "praisegod", which could
* never match a table key — so every multi-word entry would have been a
* silent no-op. Only single words are in the table today, so this was latent;
* fixing it now means a multi-word entry added later actually works.
*/
function key(text) {
return String(text || '')
.toLowerCase()
.replace(/[‘’']/g, '')
.replace(/[-‐-―]+/g, '') // A-men -> amen
.replace(/\s+/g, ' ') // collapse, do NOT delete
.replace(/[.!?,;:…。!?]+$/g, '')
.trim();
}
/*
* Confidence is recorded per language, not assumed. HIGH = a form I can state
* without hedging. REVIEW = believed correct and worth a native speaker's
* glance before a service. A language absent from a table falls through to
* NLLB — no entry is better than a confident wrong one.
*/
const REVIEW = { vie_Latn: 1, hin_Deva: 1, hat_Latn: 1, arb_Arab: 1, kor_Hang: 1, npi_Deva: 1 };
const AMEN = {
spa_Latn: 'Amén.', por_Latn: 'Amém.', por_PT: 'Amém.',
fra_Latn: 'Amen.', deu_Latn: 'Amen.', ita_Latn: 'Amen.',
nld_Latn: 'Amen.', pol_Latn: 'Amen.', ces_Latn: 'Amen.',
swe_Latn: 'Amen.', dan_Latn: 'Amen.', fin_Latn: 'Aamen.',
hun_Latn: 'Ámen.', cat_Latn: 'Amén.', ron_Latn: 'Amin.',
tur_Latn: 'Âmin.', ell_Grek: 'Αμήν.', rus_Cyrl: 'Аминь.',
ukr_Cyrl: 'Амінь.', srp_Cyrl: 'Амин.', swh_Latn: 'Amina.',
zho_Hans: '阿们。', zho_Hant: '阿們。', kor_Hang: '아멘.',
vie_Latn: 'A-men.', hat_Latn: 'Amèn.', arb_Arab: 'آمين.',
hin_Deva: 'आमीन।', urd_Arab: 'آمین۔', pes_Arab: 'آمین.'
};
const HALLELUJAH = {
spa_Latn: '¡Aleluya!', por_Latn: 'Aleluia!', por_PT: 'Aleluia!',
fra_Latn: 'Alléluia !', deu_Latn: 'Halleluja!', ita_Latn: 'Alleluia!',
nld_Latn: 'Halleluja!', pol_Latn: 'Alleluja!', ces_Latn: 'Aleluja!',
swe_Latn: 'Halleluja!', dan_Latn: 'Halleluja!', fin_Latn: 'Halleluja!',
hun_Latn: 'Halleluja!', cat_Latn: 'Al·leluia!', ron_Latn: 'Aleluia!',
ell_Grek: 'Αλληλούια!', rus_Cyrl: 'Аллилуйя!', ukr_Cyrl: 'Алілуя!',
srp_Cyrl: 'Алилуја!', zho_Hans: '哈利路亚!', zho_Hant: '哈利路亞!',
kor_Hang: '할렐루야!', vie_Latn: 'Ha-lê-lu-gia!', hat_Latn: 'Alelouya!',
arb_Arab: 'هللويا!'
};
const HOSANNA = {
spa_Latn: '¡Hosanna!', por_Latn: 'Hosana!', por_PT: 'Hosana!',
fra_Latn: 'Hosanna !', deu_Latn: 'Hosianna!', ita_Latn: 'Osanna!',
nld_Latn: 'Hosanna!', pol_Latn: 'Hosanna!', rus_Cyrl: 'Осанна!',
ell_Grek: 'Ωσαννά!', zho_Hans: '和散那!', zho_Hant: '和散那!',
kor_Hang: '호산나!', hat_Latn: 'Ozana!'
};
const PHRASES = {
amen: AMEN,
hallelujah: HALLELUJAH, halleluyah: HALLELUJAH, alleluia: HALLELUJAH,
hosanna: HOSANNA, hosana: HOSANNA
};
/* The whole utterance, or nothing. Returns null so callers fall through to
normal translation, which must stay the default path. */
/* A regional variant inherits its base language's forms. Without this a
Spain or Portugal listener falls through to NLLB for the single most
frequent utterance in the service. Canonical list: NLLB_ALIAS. */
const VARIANT_OF = { por_PT: 'por_Latn', spa_ES: 'spa_Latn', spa_MX: 'spa_Latn' };
function lookup(text, lang) {
const table = PHRASES[key(text)];
if (!table) return null;
return table[lang] || table[VARIANT_OF[lang]] || null;
}
function needsReview(lang) { return !!REVIEW[lang]; }
function phrases() { return Object.keys(PHRASES); }
return { lookup, key, needsReview, phrases, PHRASES };
}));