119 lines
5.6 KiB
JavaScript
119 lines
5.6 KiB
JavaScript
/*
|
||||
|
|
* 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 };
|
|||
|
|
}));
|