From d4ff247d48f9e1d5e86e730a10de72e38c44766b Mon Sep 17 00:00:00 2001 From: Brian Glass Date: Fri, 11 Sep 2026 09:40:55 -0400 Subject: [PATCH] Speak LXX2012+WEB in the Alexa skill instead of the King James Version The readings page has defaulted to the modern LXX2012+WEB pairing for a while; the skill was never switched and still spoke KJV. It passed no translation at all, so it fell through to bible.DEFAULT_TRANSLATIONS['en']. That default stays KJV: the API and the RSS feeds resolve through it and are deliberately unchanged, as calendarium/views.py already notes. The choice is made in alexa.speech.TRANSLATION instead, and every call site that reads scripture has to pass it, because Pericope.get_passage takes the translation per call rather than inheriting it from the Day. Six sites: three get_passage calls and the four Day constructors, the latter so that a future fetch_content=True path cannot silently fall back to KJV. Two tests pinned KJV phrasing and now pin the new text -- "deceives his heart" for "deceiveth his own heart", and "The Spirit who lives in us yearns jealously" for "The spirit that dwelleth in us lusteth to envy". Both carry the old wording in a comment so the reason for the change stays legible. help.ssml and the Alexa page both claimed the King James Version. The spoken one avoids the "LXX2012 & WEB" label, which Alexa would mangle, and names the two translations the way a listener would recognise them; the web page links them the same way the about page does. Also changes "follow OCA rubrics" to "follow OCA practices" in those two places and in the API description, matching the about page, which already says "the practice of the Orthodox Church in America". Left alone: the about page's "because the rubrics use Septuagint versification", which is the lectionary's rubrics in the technical sense rather than a claim about whose practice we follow. The API description also gained a space between two implicitly concatenated strings -- it rendered as "for a given day.The API" -- and "The API follow" became "follows". 197 tests pass. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_019nQRjJpPqW89xqzAf1vsEX --- alexa/skills.py | 12 ++++++------ alexa/speech.py | 13 +++++++++++-- alexa/tests/test_intents.py | 4 +++- alexa/tests/test_speech.py | 9 +++++++-- calendarium/api.py | 4 ++-- calendarium/templates/help.ssml | 5 +++-- orthocal/templates/alexa.html | 5 ++++- 7 files changed, 36 insertions(+), 16 deletions(-) diff --git a/alexa/skills.py b/alexa/skills.py index 5f370c97..0be8a9ad 100644 --- a/alexa/skills.py +++ b/alexa/skills.py @@ -34,7 +34,7 @@ def get_day(handler_input): else: dt = timezone.localtime() - day = liturgics.Day(dt.year, dt.month, dt.day) + day = liturgics.Day(dt.year, dt.month, dt.day, translation=speech.TRANSLATION) day.initialize() return day @@ -58,7 +58,7 @@ def handle(self, handler_input): session.clear() today = timezone.localtime() - day = liturgics.Day(today.year, today.month, today.day) + day = liturgics.Day(today.year, today.month, today.day, translation=speech.TRANSLATION) day.initialize() speech_text, card_text = speech.day_speech(day) @@ -222,7 +222,7 @@ def handle(self, handler_input): # Build speech - passage = readings[0].pericope.get_passage() + passage = readings[0].pericope.get_passage(translation=speech.TRANSLATION) group_size = speech.estimate_group_size(passage) date_text = day.gregorian_date.strftime('%A, %B %-d') reading_speech = speech.reading_speech(readings[0], group_size) @@ -302,7 +302,7 @@ def handle(self, handler_input): def commemorations_handler(self, session, builder): dt = datetime.strptime(session['date'], '%Y-%m-%d') - day = liturgics.Day(dt.year, dt.month, dt.day) + day = liturgics.Day(dt.year, dt.month, dt.day, translation=speech.TRANSLATION) day.initialize() next_commemoration = session.get('next_commemoration') @@ -342,14 +342,14 @@ def commemorations_handler(self, session, builder): def scriptures_handler(self, session, builder): dt = datetime.strptime(session['date'], '%Y-%m-%d') - day = liturgics.Day(dt.year, dt.month, dt.day) + day = liturgics.Day(dt.year, dt.month, dt.day, translation=speech.TRANSLATION) day.initialize() readings = day.get_abbreviated_readings() next_reading = session.get('next_reading') reading = readings[next_reading] - passage = reading.pericope.get_passage() + passage = reading.pericope.get_passage(translation=speech.TRANSLATION) # Is this a long passage? if group_size := session.get('group_size'): diff --git a/alexa/speech.py b/alexa/speech.py index b956dd34..75bc142e 100644 --- a/alexa/speech.py +++ b/alexa/speech.py @@ -4,10 +4,19 @@ from django.utils import timezone -from calendarium.datetools import FastLevels +from calendarium.datetools import FastLevels, Translation MAX_SPEECH_LENGTH = 8000 +# The skill speaks a modern translation, matching the readings page. +# +# It is set here rather than left to `bible.DEFAULT_TRANSLATIONS['en']`, which +# is KJV and stays that way: the API and the RSS feeds resolve through that +# default and are deliberately unchanged. `Pericope.get_passage` takes the +# translation per call and does not inherit it from the Day, so every call site +# that reads scripture has to pass it. +TRANSLATION = Translation.LXX2012WEB + ref_re = re.compile(r'(\d*)\s*([\w\s]+)\s+(\d+)') ssml_re = re.compile(r'<(?!p\b)(.*?)>(.*?)') @@ -209,7 +218,7 @@ def reading_speech(reading, end=None): ) def reading_range_speech(reading, start=None, end=None): - passage = reading.pericope.get_passage() + passage = reading.pericope.get_passage(translation=TRANSLATION) return '\n'.join(f'

{verse.content}

' for verse in passage[start:end]) def estimate_group_size(passage): diff --git a/alexa/tests/test_intents.py b/alexa/tests/test_intents.py index 4fe91277..f2988987 100644 --- a/alexa/tests/test_intents.py +++ b/alexa/tests/test_intents.py @@ -54,7 +54,9 @@ def test_next_intent(self): request_envelope = skill.serializer.deserialize(payload=envelope, obj_type=RequestEnvelope) response = skill.invoke(request_envelope=request_envelope, context=None) - self.assertIn('deceiveth his own heart', response.response.output_speech.ssml) + # LXX2012-WEB, the translation the skill speaks -- KJV has + # "deceiveth his own heart" here. See alexa.speech.TRANSLATION. + self.assertIn('deceives his heart', response.response.output_speech.ssml) self.assertIn('Would you like to hear the next reading?', response.response.output_speech.ssml) self.assertEqual(1, response.session_attributes['next_reading']) self.assertEqual(['commemorations'], response.session_attributes['task_queue']) diff --git a/alexa/tests/test_speech.py b/alexa/tests/test_speech.py index 571e3044..1dab233f 100644 --- a/alexa/tests/test_speech.py +++ b/alexa/tests/test_speech.py @@ -131,9 +131,14 @@ def test_reference_speech(self): self.assertEqual(expected, actual) def test_reading_speech(self): - day = liturgics.Day(2023, 1, 18) + """The skill speaks LXX2012-WEB, not the KJV that + `bible.DEFAULT_TRANSLATIONS['en']` still gives the API and the feeds. + KJV renders this verse "The spirit that dwelleth in us lusteth to + envy"; see alexa.speech.TRANSLATION.""" + + day = liturgics.Day(2023, 1, 18, translation=speech.TRANSLATION) day.initialize() readings = day.get_readings() speech_text = speech.reading_speech(readings[0]) - self.assertIn('The spirit that dwelleth in us lusteth to envy', speech_text) + self.assertIn('The Spirit who lives in us yearns jealously', speech_text) diff --git a/calendarium/api.py b/calendarium/api.py index 3d3f5842..1b3f1974 100644 --- a/calendarium/api.py +++ b/calendarium/api.py @@ -95,8 +95,8 @@ def get_openapi_operation_id(self, operation): description=( 'Orthocal.info provides an API for looking up information about ' 'days and months in the Orthodox Calendar, including the ability ' - 'to look up the scripture readings and lives of the saints for a given day.' - 'The API follow OCA rubrics.' + 'to look up the scripture readings and lives of the saints for a given day. ' + 'The API follows OCA practices.' ), servers=[ {'url': settings.ORTHOCAL_PUBLIC_URL, 'description': 'Public API'}, diff --git a/calendarium/templates/help.ssml b/calendarium/templates/help.ssml index 8544e052..8727fa9b 100644 --- a/calendarium/templates/help.ssml +++ b/calendarium/templates/help.ssml @@ -2,8 +2,9 @@

Orthodox Daily makes it easy to access the daily scripture readings. Simply ask me to open Orthodox Daily and I will provide you with a few details about today, including the fasting rules, and then read the prescribed -scriptures. The scriptures are read from the King James Version and at -present follow OCA rubrics.

+scriptures. The scriptures are read from the World English Bible, with an Old +Testament translated from the Septuagint, and at present follow +OCA practices.

You can also ask me directly about a particular day. For instance you can say, "Alexa, ask Orthodox Daily about tomorrow." Or you could say, diff --git a/orthocal/templates/alexa.html b/orthocal/templates/alexa.html index 2c35a62e..c0b0f9cd 100644 --- a/orthocal/templates/alexa.html +++ b/orthocal/templates/alexa.html @@ -14,7 +14,10 @@

Alexa Skill

scripture readings and commemorations. Simply ask Alexa to open Orthodox Daily, and she will provide you with a few details about today, including the fasting rules, and then read the prescribed scriptures and lives of the saints out loud. - The scriptures are read from the King James Version and follow OCA rubrics + The scriptures are read from + LXX2012 + + WEB, the same + modern-English pairing the readings page uses by default, and follow OCA practices and fasting guidelines. For the Alexa skill, the number of readings is abbreviated. A fuller list is available on this website. The stories from the lives of the saints are borrowed from abbamoses.com by