Add the KJV, ASV and LSV (free helloao translations; savable offline)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-14 15:14:25 -04:00
1 parent 6a89ee058f
commit d5cc93f9bd
5 files changed
+28 -3

No files matched your search

+1 -1
View File
@@ -106,7 +106,7 @@ export default function DataSection() {
<p className="mb-3 mt-1 text-sm text-[var(--r-sub)]">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.</p>
{offline.job ? <DownloadProgress job={offline.job} onCancel={offline.cancel} /> : (
<div className="flex flex-wrap gap-3">
{['BSB', 'NET'].map((t) => (
{['BSB', 'NET', 'KJV', 'ASV', 'LSV'].map((t) => (
<button key={t} type="button" onClick={() => offline.saveBible(t)} className={btn}>
{(offline.saved[t] ?? []).length >= 66 ? `✓ The whole ${t} is saved` : `Save the whole ${t}`}
</button>
+5
View File
@@ -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;
+1 -1
View File
@@ -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(),
}));
}
+18
View File
@@ -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');
});
+3 -1
View File
@@ -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 });