From d5cc93f9bd14b90a6a6648a9486b9c9553ac502e Mon Sep 17 00:00:00 2001 From: nmemmert <67961292+nmemmert@users.noreply.github.com> Date: Wed, 14 Oct 2026 15:14:25 -0400 Subject: [PATCH] Add the KJV, ASV and LSV (free helloao translations; savable offline) Co-Authored-By: Claude Sonnet 5.5 --- src/components/settings/DataSection.jsx | 2 +- src/utils/bibleText.js | 5 +++++ src/utils/bibleUtils.jsx | 2 +- src/utils/offline.test.js | 18 ++++++++++++++++++ src/utils/studyText.js | 4 +++- 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/components/settings/DataSection.jsx b/src/components/settings/DataSection.jsx index f23386d..2d0c840 100644 --- a/src/components/settings/DataSection.jsx +++ b/src/components/settings/DataSection.jsx @@ -106,7 +106,7 @@ export default function DataSection() {

Chapters you read are kept automatically. To read anywhere, save a whole Bible while you’re connected (about 4 MB). The ESV, NLT, CSB and NASB can’t be saved: their licences don’t allow it.

{offline.job ? : (
- {['BSB', 'NET'].map((t) => ( + {['BSB', 'NET', 'KJV', 'ASV', 'LSV'].map((t) => ( diff --git a/src/utils/bibleText.js b/src/utils/bibleText.js index 3b2ba90..d491971 100644 --- a/src/utils/bibleText.js +++ b/src/utils/bibleText.js @@ -8,6 +8,9 @@ export const READER_TRANSLATIONS = [ notice: 'Scripture quotations are from the ESV® Bible (The Holy Bible, English Standard Version®), © 2001 by Crossway, a publishing ministry of Good News Publishers. Used by permission. All rights reserved.' }, { id: 'NET', name: 'NET Bible', helloao: 'eng_net', notice: 'Scripture quoted by permission. Quotations designated (NET) are from the NET Bible® copyright ©1996-2017 by Biblical Studies Press, L.L.C. http://netbible.com All rights reserved.' }, + { id: 'KJV', name: 'King James Version', helloao: 'eng_kjv', notice: 'The King James Version is in the public domain.' }, + { id: 'ASV', name: 'American Standard Version (1901)', helloao: 'eng_asv', notice: 'The American Standard Version (1901) is in the public domain.' }, + { id: 'LSV', name: 'Literal Standard Version', helloao: 'eng_lsv', notice: 'Literal Standard Version © 2020 Covenant Press and the Covenant Christian Coalition. Free to read and share. lsvbible.com' }, { id: 'NLT', name: 'New Living Translation', licensed: true, notice: 'Holy Bible, New Living Translation, Copyright © 2014, Tyndale House Publishers. All rights reserved. tyndale.com' }, { id: 'CSB', name: 'Christian Standard Bible', licensed: true, @@ -16,6 +19,8 @@ export const READER_TRANSLATIONS = [ notice: 'New American Standard Bible®, Copyright © 1995, The Lockman Foundation. All rights reserved. lockman.org' }, ]; +// The address code helloao uses for a translation (NET is 'eng_net', the KJV 'eng_kjv'; BSB is its own) +export const helloaoCode = (id) => READER_TRANSLATIONS.find((t) => t.id === id)?.helloao ?? id ?? 'BSB'; export const translationName = (id) => READER_TRANSLATIONS.find((t) => t.id === id)?.name ?? id; export const isLicensed = (id) => READER_TRANSLATIONS.find((t) => t.id === id)?.licensed === true; diff --git a/src/utils/bibleUtils.jsx b/src/utils/bibleUtils.jsx index 922ede7..61ca529 100644 --- a/src/utils/bibleUtils.jsx +++ b/src/utils/bibleUtils.jsx @@ -485,7 +485,7 @@ export function parseBibleChapter(data) { .filter((item) => item?.type === 'verse') .map((item) => ({ number: item.number, - text: renderVerseContent(item.content).replace(/\s+/g, ' ').trim(), + text: renderVerseContent(item.content).replace(/¶\s*/g, '').replace(/\s+/g, ' ').trim(), })); } diff --git a/src/utils/offline.test.js b/src/utils/offline.test.js index 6631345..0225b1d 100644 --- a/src/utils/offline.test.js +++ b/src/utils/offline.test.js @@ -57,3 +57,21 @@ test('a study works offline once every book in it is saved in its translation', test('licensed translations cannot be saved offline', () => { expect(offlineStatus({ translation: 'ESV', chapters: [{ book: 'John', bookAbbrev: 'JHN' }] }, {}).state).toBe('licensed'); }); + +import { READER_TRANSLATIONS, helloaoCode } from './bibleText.js'; +import { parseBibleChapter } from './bibleUtils.jsx'; + +test('the KJV, ASV and LSV are free translations that can be saved offline and are fetched by their helloao code', () => { + for (const [id, code] of [['KJV', 'eng_kjv'], ['ASV', 'eng_asv'], ['LSV', 'eng_lsv'], ['NET', 'eng_net'], ['BSB', 'BSB']]) { + expect(READER_TRANSLATIONS.some((t) => t.id === id)).toBe(true); + expect(helloaoCode(id)).toBe(code); + expect(canSaveOffline(id)).toBe(true); + } + expect(canSaveOffline('ESV')).toBe(false); + expect(READER_TRANSLATIONS.find((t) => t.id === 'LSV').notice).toMatch(/Covenant Press/); +}); + +test('a paragraph mark in the KJV text is dropped from the verse', () => { + const verses = parseBibleChapter({ chapter: { content: [{ type: 'verse', number: 16, content: [{ text: '¶ For God so loved the world' }] }] } }); + expect(verses[0].text).toBe('For God so loved the world'); +}); diff --git a/src/utils/studyText.js b/src/utils/studyText.js index 5aaa818..7b6b42e 100644 --- a/src/utils/studyText.js +++ b/src/utils/studyText.js @@ -1,8 +1,10 @@ // Fetches a chapter's raw text for building a study (the free Bible source). The Reader's "Save for // offline" downloads go through the same address, so a saved book builds a study with no connection. // Failures say what's wrong and what to do, instead of a generic error. +import { helloaoCode } from './bibleText.js'; + export async function fetchStudyChapter(translation, bookAbbrev, chapter, { signal } = {}) { - const code = translation === 'NET' ? 'eng_net' : (translation || 'BSB'); + const code = helloaoCode(translation || 'BSB'); let res; try { res = await fetch(`https://bible.helloao.org/api/${code}/${bookAbbrev}/${chapter}.json`, { signal });