跳去內容

模組:引聖經

出自維基百科,自由嘅百科全書
模組解[] [] [] []

概要

[編輯]

{{#invoke:引聖經| cite | [ 書卷章節 ]] | 其他參數... }}

{{#invoke:引聖經| cite | [ 書卷 [ | 章節 [ | 譯本代號 ]]] | 其他參數... }}

{{#invoke:引聖經| cite2 }}

{{#invoke:引聖經| dump_codes }}

{{#invoke:引聖經| list_books | 譯本代號 }}

{{#invoke:引聖經| list_versions [ | conjunction=連接詞 ] }}

用途

[編輯]

正常引用

[編輯]

呢個模組通常係透過引聖經模間接叫用 cite2 函數,所有參數用文章版面(parent frame)畀嘅模參數。參數詳情請睇原碼或者睇引聖經模嘅模解

喺版面用 #invoke 直接叫用 cite 函數都得;兩個函數嘅功能完全一樣,分別只係 cite 函數用嘅係 #invoke 巨集畀嘅參數(current frame)。

分開寫書卷同章節本來係模擬英維同中維嘅 Bibleverse 模,模擬功能應該已經正常運作。

模解版用

[編輯]

模組提供一個 dump_codes 函數,用嚟畀模解版直接叫用,將模組內部用嘅書卷代碼同對應嘅書卷名列哂出嚟。咁做係因為如果要要喺模解版寫哂啲代碼出嚟,萬一代碼有變,模解好易會過時出錯。

dump_versions 函數亦都係用嚟畀模解版直接叫用,將模組內部用嘅譯本代碼同(如果有)對應嘅譯本名列哂出嚟,但係輸出係句子形式,可以指定連接詞,唔指定嘅話連接詞係 「同」。

其他

[編輯]

模組另外提供一個 list_books 函數係用嚟畀個別譯本嘅文直接叫用,表嘅格式係參考聖經和合本呢篇文。

本地化

[編輯]

呢個模組係粵維原創;除咗程式註解同少量錯誤訊息之外,應該已經係粵語。

睇埋

[編輯]

-- vi:set sw=4 ts=4 ai sm:
---- Bible citation module designed for Cantonese Wikipedia
---- This module implements the backend logic for Template:引聖經

local NumberToChinese = require('模組:NumberToChinese');	-- broken module that defines global functions, must be loaded first

require ('strict');
local p = {};
local Syu1meng2 = require('Module:書名');

-- Table derived from  [[舊約聖經]]、[[新約聖經]]
-- Keys are as given in CSB; unless the book doesn't exist in Protestant
-- bibles, in which case they are as given in NAB but with spaces removed.
-- Both CSB and NAB use short abbreviations (e.g., Gn instead of Gen), but
-- the abbreviations don't always match
-- Latin names from Biblical and Theological Dictionary of Christianity
local traditions = {
	['protestant'] = '基督教';
	['catholic'] = '天主教';
	['orthodox'] = '東正教';
	['english'] = '英文';		-- FIXME
	['judaism'] = '猶太教';
	['greek'] = '';	-- for TR, etc. - no label
	['latin'] = '';	-- for Vulgate - no label
}
local languages = {
	-- traditions
	['protestant'] = '中文';
	['catholic'] = '中文';
	['orthodox'] = '中文';
	['english'] = '英文';		-- FIXME
	['judaism'] = '希伯來文';		-- for testing
	-- special cases
	['greek'] = '古希臘文';
	['latin'] = '拉丁文';
	-- languages
	['ENGLISH'] = 'english';
	['HEBREW'] = 'hebrew';
}
local stems = {
	['A'] = '甲';
	['B'] = '乙';
	['C'] = '丙';
	['D'] = '丁';
	['E'] = '戊';
	['F'] = '己';
	['G'] = '庚';
	['H'] = '辛';
	['I'] = '壬';
	['J'] = '癸';
}
local numbering = {
	['protestant'] = {
			['default'] = 'CUV';
		};
	['catholic'] = {
			['default'] = 'SBV';
			['kanji_chapters_p'] = true;
		};
	['orthodox'] = {
		};
	['greek'] = {
		};
	['latin'] = {
		};
	['english'] = {
			['default'] = 'NIV';
		};
	['judaism'] = {		-- for testing
			['default'] = 'JPS';
		};
}
local books = {
	['Gn'] = {
		['order'] = 100,
		['protestant'] = { '創世記', '創' },
		['catholic'] = { '創世紀', '創' },
		['orthodox'] = { '起源之書' },
		['english'] = { 'Genesis', 'Gen.' },
		['hebrew'] = { 'בראשית' },
		['latin'] = { 'Genesis' },
		['div'] = '章',
	},
	['Ex'] = {
		['order'] = 110,
		['protestant'] = { '出埃及記', '出' },
		['catholic'] = { '出谷紀', '出' },
		['orthodox'] = { '出離之書' },
		['english'] = { 'Exodus' },
		['hebrew'] = { 'שמות' },
		['latin'] = { 'Exodus' },
		['div'] = '章',
	},
	['Lv'] = {
		['order'] = 120,
		['protestant'] = { '利未記', '利' },
		['catholic'] = { '肋未紀', '肋' },
		['orthodox'] = { '勒維人之書' },
		['english'] = { 'Leviticus' },
		['hebrew'] = { 'ויקרא' },
		['latin'] = { 'Leviticus' },
		['div'] = '章',
	},
	['Nm'] = {
		['order'] = 130,
		['protestant'] = { '民數記', '民' },
		['catholic'] = { '戶籍紀', '戶' },
		['orthodox'] = { '民數之書' },
		['english'] = { 'Numbers' },
		['hebrew'] = { 'במדבר' },
		['latin'] = { 'Numeri' },
		['div'] = '章',
	},
	['Dt'] = {
		['order'] = 140,
		['protestant'] = { '申命記', '申' },
		['catholic'] = { '申命紀', '申' },
		['orthodox'] = { '第二法典之書' },
		['english'] = { 'Deuteronomy', 'Deut.' },
		['hebrew'] = { 'דברים' },
		['latin'] = { 'Deuteronomium' },
		['div'] = '章',
	},
	['Jos'] = {
		['order'] = 150,
		['protestant'] = { '約書亞記', '書' },
		['catholic'] = { '若蘇厄書', '蘇' },
		['orthodox'] = { '納維之子伊穌斯傳' },
		['english'] = { 'Joshua' },
		['hebrew'] = { 'יהושע' },
		['latin'] = { 'Iosue' },
		['div'] = '章',
	},
	['Jdg'] = {
		['order'] = 160,
		['protestant'] = { '士師記', '士' },
		['catholic'] = { '民長紀', '民' },
		['orthodox'] = { '眾審判者傳' },
		['english'] = { 'Judges', 'Jgs.' },
		['hebrew'] = { 'שופטים' },
		['latin'] = { 'Iudicum' },
		['div'] = '章',
	},
	['Ru'] = {
		['order'] = 170,
		['protestant'] = { '路得記', '得' },
		['catholic'] = { '盧德傳', '盧' },
		['orthodox'] = { '如特傳' },
		['english'] = { 'Ruth' },
		['hebrew'] = { 'רות' },
		['latin'] = { 'Ruth' },
		['div'] = '章',
	},
	['x-Sm'] = {	-- XXX for testing
		['order'] = 175,
		['english'] = { 'Samuel' },
		['hebrew'] = { 'שמואל' },
		['parts'] = { '1Sm', '2Sm' },
	},
	['1Sm'] = {
		['order'] = 180,
		['protestant'] = { '撒母耳記上', '撒上' },
		['catholic'] = { '撒慕爾紀上', '撒上' },
		['orthodox'] = { '眾王傳一', '1 Kings' },
		['english'] = { '1 Samuel' },
		['hebrew'] = { 'שמואל א' },	-- In theory it’s just שמואל, in practice (e.g., in the Jerusalem Bible) it's split into א and ב
		['latin'] = { 'Samuelis I' },
		['div'] = '章',
	},
	['2Sm'] = {
		['order'] = 190,
		['protestant'] = { '撒母耳記下', '撒下' },
		['catholic'] = { '撒慕爾紀下', '撒下' },
		['orthodox'] = { '眾王傳二', '2 Kings' },
		['english'] = { '2 Samuel' },
		['hebrew'] = { 'שמואל ב' },
		['latin'] = { 'Samuelis II' },
		['div'] = '章',
	},
	['x-Kg'] = {	-- XXX for testing
		['order'] = 195,
		['english'] = { 'Kings' },
		['hebrew'] = { 'מלכים' },
		['parts'] = { '1Kg', '2Kg' },
	},
	['1Kg'] = {
		['order'] = 200,
		['protestant'] = { '列王紀上', '王上' },
		['catholic'] = { '列王紀上', '列上' },
		['orthodox'] = { '眾王傳三', '3 Kings' },
		['english'] = { '1 Kings', '1 Kgs.' },
		['hebrew'] = { 'מלכים א' },	-- see note at 1Sm
		['latin'] = { 'Regum I' },
		['div'] = '章',
	},
	['2Kg'] = {
		['order'] = 210,
		['protestant'] = { '列王紀下', '王下' },
		['catholic'] = { '列王紀下', '列下' },
		['orthodox'] = { '眾王傳四', '4 Kings' },
		['english'] = { '2 Kings', '2 Kgs.' },
		['hebrew'] = { 'מלכים ב' },
		['latin'] = { 'Regum II' },
		['div'] = '章',
	},
	['x-Ch'] = {	-- XXX for testing
		['order'] = 215,
		['english'] = { 'Chronicles' },
		['hebrew'] = { 'דברי הימים' },
		['parts'] = { '1Ch', '2Ch' },
	},
	['1Ch'] = {
		['order'] = 220,
		['protestant'] = { '歷代志上', '代上' },
		['catholic'] = { '編年紀上', '編上' },
		['orthodox'] = { '史書補遺一', '紀年書一' },
		['english'] = { '1 Chronicles', '1 Chr.' },
		['hebrew'] = { 'דברי הימים א' },	-- see note at 1Sm
		['latin'] = { 'Paralipomenon I' },
		['div'] = '章',
	},
	['2Ch'] = {
		['order'] = 230,
		['protestant'] = { '歷代志下', '代下' },
		['catholic'] = { '編年紀下', '編下' },
		['orthodox'] = { '史書補遺二', '紀年書二' },
		['english'] = { '2 Chronicles', '2 Chr.' },
		['hebrew'] = { 'דברי הימים ב' },
		['latin'] = { 'Paralipomenon II' },
		['div'] = '章',
	},
	['PrMan'] = {
		['order'] = 231,	-- goes here according to enwiki
		--XXX['protestant'] = { '瑪拿西禱文' },
		['english'] = { 'Prayer of Manasseh' },
		['latin'] = { 'Oratio Manassae' },
		['div'] = '節',
	},
	['x-Ezr-Neh'] = {	-- XXX for testing
		['order'] = 235,
		['english'] = { 'Ezra-Nehemiah' },
		['hebrew'] = { 'עזרא־נחמיה' },
		['parts'] = { 'Ezr', 'Neh' },
	},
	['3Esd'] = {		-- per Lo et al. (2003)
		['order'] = 240,
		--XXX['protestant'] = { '以斯得拉一書' },
		['orthodox'] = { '艾斯德拉紀一' },
		['english'] = { '1 Esdras' },
		['hebrew'] = { 'עזרא החיצוני' },
		['latin'] = { 'Esdrae III' },
		['div'] = '章',
	},
	['4Esd'] = {		-- per Lo et al. (2003)
		['order'] = 241,
		--XXX['protestant'] = { '以斯得拉二書' },
		['english'] = { '2 Esdras' },
		['latin'] = { 'Esdrae IV' },
		['div'] = '章',
	},
	['Ezr'] = {
		['order'] = 250,
		['protestant'] = { '以斯拉記', '拉' },
		['catholic'] = { '厄斯德拉上', '厄上', '厄斯德拉卷上' },
		['orthodox'] = { '艾斯德拉紀二' },
		['english'] = { 'Ezra' },
		['hebrew'] = { 'עזרא' },
		['latin'] = { 'Esdrae' },
		['div'] = '章',
	},
	['Neh'] = {
		['order'] = 260,
		['protestant'] = { '尼希米記', '尼' },
		['catholic'] = { '厄斯德拉下', '厄下', '厄斯德拉卷下', '乃赫米雅', '乃赫米雅書' },
		['orthodox'] = { '奈俄彌亞紀' },
		['english'] = { 'Nehemiah' },
		['hebrew'] = { 'נחמיה' },
		['latin'] = { 'Nehemiae' },
		['div'] = '章',
	},
	['Tb'] = {
		['order'] = 270,
		['catholic'] = { '多俾亞傳', '多' },
		['orthodox'] = { '托維特傳' },
		['english'] = { 'Tobit' },
		['hebrew'] = { 'טובי' },
		['latin'] = { 'Tobiae' },
		['div'] = '章',
	},
	['Jdt'] = {
		['order'] = 280,
		['catholic'] = { '友弟德傳', '友' },
		['orthodox'] = { '虞狄特傳' },
		['english'] = { 'Judith' },
		['hebrew'] = { 'יהודית' },
		['latin'] = { 'Iudith' },
		['div'] = '章',
	},
	['Est'] = {
		['order'] = 290,
		['protestant'] = { '以斯帖記', '斯' },
		['catholic'] = { '艾斯德爾傳', '艾' },
		['orthodox'] = { '艾斯提爾傳' },
		['english'] = { 'Esther' },
		['hebrew'] = { 'אסתר' },
		['latin'] = { 'Esther' },
		['div'] = '章',
		['embeds'] = {
			['A'] = 1,
			['B'] = 3,
			['C'] = 4,
			['D'] = 4,
			['E'] = 8,	-- this is a trainwreck: 8:1-12 is ok, SBV 8:13-17 has no heading. NAB E, SBV E have different verse breaks
			['F'] = 10,
		},
	},
	['1Mc'] = {
		['order'] = 300,
		['catholic'] = { '瑪加伯上', '加上', '{{full}}瑪加伯書上' },	-- see note below
		['orthodox'] = { '瑪喀維傳一' },
		['english'] = { '1 Maccabees' },
		['hebrew'] = { 'מקבים א' },
		['latin'] = { 'Machabaeorum I' },
		['div'] = '章',
	},
	['2Mc'] = {
		['order'] = 310,
		['catholic'] = { '瑪加伯下', '加下',
				'{{full}}瑪加伯書下' }, -- v. 'https://fatimacc.catholic.org.hk/daily.reading.2021/2021.11/20211116.pdf
		['orthodox'] = { '瑪喀維傳二' },
		['english'] = { '2 Maccabees' },
		['hebrew'] = { 'מקבים ב' },
		['latin'] = { 'Machabaeorum II' },
		['div'] = '章',
	},
	['3Mc'] = {
		['order'] = 320,
		['orthodox'] = { '瑪喀維傳三' },
		['english'] = { '3 Maccabees' },
		['hebrew'] = { 'מקבים ג' },
		['div'] = '章',
	},
	['4Mc'] = {
		['order'] = 330,
		['orthodox'] = { '瑪喀維傳四(附錄)', '瑪喀維傳四' },
		['english'] = { '4 Maccabees' },
		['hebrew'] = { 'מקבים ד' },
		['div'] = '章',
	},
	['Jb'] = {
		['order'] = 340,
		['protestant'] = { '約伯記', '伯' },
		['catholic'] = { '約伯傳', '約' },
		['orthodox'] = { '約弗傳' },
		['english'] = { 'Job' },
		['hebrew'] = { 'איוב' },
		['latin'] = { 'Iob' },
		['div'] = '章',
	},
	['Ps'] = {
		['order'] = 350,
		['protestant'] = { '詩篇', '詩' },
		['catholic'] = { '{{plural}}聖詠集', '詠', '{{singular}}聖詠' },
		['orthodox'] = { '聖詠集' },
		['english'] = { 'Psalms', 'Pss.' },
		['hebrew'] = { 'תהלים' },
		['latin'] = { 'Psalmi' },
		['div'] = '篇',
	},
	['Pr'] = {
		['order'] = 360,
		['protestant'] = { '箴言', '箴' },
		['catholic'] = { '箴言', '箴' },
		['orthodox'] = { '索洛蒙箴言' },
		['english'] = { 'Proverbs', 'Prv.', 'Prov.' },
		['hebrew'] = { 'משלי' },
		['latin'] = { 'Proverbia' },
		['div'] = '章',
	},
	['Ec'] = {
		['order'] = 370,
		['protestant'] = { '傳道書', '傳' },
		['catholic'] = { '訓道篇', '訓' },
		['orthodox'] = { '訓道篇' },
		['english'] = { 'Ecclesiastes', 'Eccl.' },
		['hebrew'] = { 'קהלת' },
		['latin'] = { 'Ecclesiastes' },
		['div'] = '章',
	},
	['Sg'] = {
		['order'] = 380,
		['protestant'] = { '雅歌', '歌' },
		['catholic'] = { '雅歌', '歌' },
		['orthodox'] = { '歌中之歌' },
		['english'] = { 'Song of Songs' },
		['hebrew'] = { 'שיר השירים' },
		['latin'] = { 'Canticum Canticorum' },
		['div'] = '章',
	},
	['Wis'] = {
		['order'] = 390,
		--XXX['protestant'] = { '所羅門智訓' },
		['catholic'] = { '智慧篇', '智' },
		['orthodox'] = { '索洛蒙的智慧書' },
		['english'] = { 'Wisdom' },
		['hebrew'] = { 'חכמת שלמה' },
		['latin'] = { 'Sapientia' },
		['div'] = '章',
	},
	['Sir'] = {
		['order'] = 400,
		--XXX['protestant'] = { '傳道經' },
		['catholic'] = { '德訓篇', '德' },
		['orthodox'] = { '希拉赫的智慧書' },
		['english'] = { 'Sirach', 'Ecclesiasticus' },
		['hebrew'] = { 'בן סירא' },
		['latin'] = { 'Ecclesiasticus' },
		['div'] = '章',
	},
	['Is'] = {
		['order'] = 410,
		['protestant'] = { '以賽亞書', '賽' },
		['catholic'] = { '依撒意亞', '依', '{{full}}依撒意亞先知書' },
		['orthodox'] = { '伊撒依亞書' },
		['english'] = { 'Isaiah' },
		['hebrew'] = { 'ישעיהו' },
		['latin'] = { 'Isaias' },
		['div'] = '章',
	},
	['Jr'] = {
		['order'] = 420,
		['protestant'] = { '耶利米書', '耶' },
		['catholic'] = { '耶肋米亞', '耶', '{{full}}耶肋米亞先知書' },
		['orthodox'] = { '耶熱彌亞書' },
		['english'] = { 'Jeremiah', 'Jer.' },
		['hebrew'] = { 'ירמיהו' },
		['latin'] = { 'Ieremias' },
		['div'] = '章',
	},
	['Lm'] = {
		['order'] = 430,
		['protestant'] = { '耶利米哀歌', '哀' },
		['catholic'] = { '哀歌', '哀', '{{full}}耶肋米亞哀歌' },
		['orthodox'] = { '耶熱彌亞之哀歌' },
		['english'] = { 'Lamentations', 'Lam.' },
		['hebrew'] = { 'יחזקאל' },
		['latin'] = { 'Lamentationes' },
		['div'] = '章',
	},
	['Bar'] = {
		['order'] = 440,
		---XXX['protestant'] = { '巴錄書' },
		['catholic'] = { '巴路克', '巴', '{{full}}巴路克先知書' },
		['orthodox'] = { '瓦如赫書' },
		['english'] = { 'Baruch' },
		['hebrew'] = { 'ברוך' },
		['latin'] = { 'Baruch' },
		['div'] = '章',
	},
	['x-LtJr'] = {--XXX made-up code
		['order'] = 450,
		['orthodox'] = { '耶熱彌亞之書信' },
		['english'] = { 'Letter of Jeremiah' },
		['hebrew'] = { 'איגרת ירמיהו' },
		['div'] = '章',
	},
	['Ezk'] = {
		['order'] = 460,
		['protestant'] = { '以西結書', '結' },
		['catholic'] = { '厄則克耳', '則', '{{full}}厄則克耳先知書'},
		['orthodox'] = { '耶則基伊爾書' },
		['english'] = { 'Ezekiel', 'Ez.' },
		['hebrew'] = { 'יחזקאל' },
		['latin'] = { 'Ezechiel' },
		['div'] = '章',
	},
	['Dn'] = {
		['order'] = 470,
		['protestant'] = { '但以理書', '但' },
		['catholic'] = { '達尼爾', '達', '{{full}}達尼爾先知書' },
		['orthodox'] = { '達尼伊爾書' },
		['english'] = { 'Daniel' },
		['hebrew'] = { 'דניאל' },
		['latin'] = { 'Daniel' },
		['div'] = '章',
	},
	['Hs'] = {
		['order'] = 480,
		['protestant'] = { '何西阿書', '何' },
		['catholic'] = { '歐瑟亞', '歐', '{{full}}歐瑟亞先知書' },
		['orthodox'] = { '奧西埃書' },
		['english'] = { 'Hosea', 'Hos.' },
		['hebrew'] = { 'הושע' },
		['latin'] = { 'Osee' },
		['div'] = '章',
	},
	['Jl'] = {
		['order'] = 490,
		['protestant'] = { '約珥書', '珥' },
		['catholic'] = { '岳厄爾', '岳', '{{full}}岳厄爾先知書' },
		['orthodox'] = { '約伊爾書' },
		['english'] = { 'Joel', 'Hos.' },
		['hebrew'] = { 'יואל' },
		['latin'] = { 'Ioel' },
		['div'] = '章',
	},
	['Am'] = {
		['order'] = 500,
		['protestant'] = { '阿摩司書', '摩' },
		['catholic'] = { '亞毛斯', '亞', '{{full}}亞毛斯先知書' },
		['orthodox'] = { '阿摩斯書' },
		['english'] = { 'Amos' },
		['hebrew'] = { 'עמוס' },
		['latin'] = { 'Amos' },
		['div'] = '章',
	},
	['Ob'] = {
		['order'] = 510,
		['protestant'] = { '俄巴底亞書', '俄' },
		['catholic'] = { '亞北底亞', '北', '{{full}}亞北底亞先知書' },
		['orthodox'] = { '奧弗狄亞書' },
		['english'] = { 'Obadiah' },
		['hebrew'] = { 'עבדיה' },
		['latin'] = { 'Abdias' },
		['div'] = '節',
	},
	['Jnh'] = {
		['order'] = 520,
		['protestant'] = { '約拿書', '拿' },
		['catholic'] = { '約納', '納', '{{full}}約納先知書' },
		['orthodox'] = { '約納書' },
		['english'] = { 'Jonah', 'Jon.' },
		['hebrew'] = { 'יונה' },
		['latin'] = { 'Ionas' },
		['div'] = '章',
	},
	['Mc'] = {
		['order'] = 530,
		['protestant'] = { '彌迦書', '彌' },
		['catholic'] = { '米該亞', '米', '{{full}}米該亞先知書' },
		['orthodox'] = { '彌亥亞書' },
		['english'] = { 'Micah', 'Mi.' },
		['hebrew'] = { 'מיכה' },
		['latin'] = { 'Michaeas' },
		['div'] = '章',
	},
	['Nah'] = {
		['order'] = 540,
		['protestant'] = { '那鴻書', '鴻' },
		['catholic'] = { '納鴻', '鴻', '{{full}}納鴻先知書' },
		['orthodox'] = { '納翁書' },
		['english'] = { 'Nahum', 'Na.' },
		['hebrew'] = { 'נחום' },
		['latin'] = { 'Nahum' },
		['div'] = '章',
	},
	['Hab'] = {
		['order'] = 550,
		['protestant'] = { '哈巴谷書', '哈' },
		['catholic'] = { '哈巴谷', '哈', '{{full}}哈巴谷先知書' },
		['orthodox'] = { '盎瓦庫穆書' },
		['english'] = { 'Habakkuk', 'Hb.' },
		['hebrew'] = { 'חבקוק' },
		['latin'] = { 'Habacuc' },
		['div'] = '章',
	},
	['Zph'] = {
		['order'] = 560,
		['protestant'] = { '西番雅書', '番' },
		['catholic'] = { '索福尼亞', '索', '{{full}}索福尼亞先知書' },
		['orthodox'] = { '索佛尼亞書' },
		['english'] = { 'Zephaniah', 'Zep.' },
		['hebrew'] = { 'צפניה' },
		['latin'] = { 'Sophonias' },
		['div'] = '章',
	},
	['Hg'] = {
		['order'] = 570,
		['protestant'] = { '哈該書', '該' },
		['catholic'] = { '哈蓋', '蓋', '{{full}}哈蓋先知書' },
		['orthodox'] = { '盎蓋書' },
		['english'] = { 'Haggai' },
		['hebrew'] = { 'חגי' },
		['latin'] = { 'Aggaeus' },
		['div'] = '章',
	},
	['Zch'] = {
		['order'] = 580,
		['protestant'] = { '撒迦利亞書', '亞' },
		['catholic'] = { '匝加利亞', '匝', '{{full}}匝加利亞先知書' },
		['orthodox'] = { '匝哈里亞書' },
		['english'] = { 'Zechariah', 'Zec.' },
		['hebrew'] = { 'זכריה' },
		['latin'] = { 'Zacharias' },
		['div'] = '章',
	},
	['Mal'] = {
		['order'] = 590,
		['protestant'] = { '瑪拉基書', '瑪' },
		['catholic'] = { '瑪拉基亞', '拉', '{{full}}瑪拉基亞先知書' },
		['orthodox'] = { '瑪拉希亞書' },
		['english'] = { 'Malachi' },
		['hebrew'] = { 'מלאכי' },
		['latin'] = { 'Malachias' },
		['div'] = '章',
	},
	['Mt'] = {
		['order'] = 600,
		['protestant'] = { '馬太福音', '太' },
		['catholic'] = { '瑪竇福音', '瑪', '{{full}}聖瑪竇福音' },
		['orthodox'] = { '聖福音依瑪特泰所傳者' },
		['english'] = { 'Matthew', 'Matt.' },
		['greek'] = { 'Κατά Ματθαίον' },
		['latin'] = { 'Matthaeus' },
		['div'] = '章',
	},
	['Mk'] = {
		['order'] = 610,
		['protestant'] = { '馬可福音', '可' },
		['catholic'] = { '馬爾谷福音', '谷', '{{full}}聖馬爾谷福音' },
		['orthodox'] = { '聖福音依瑪爾克所傳者' },
		['english'] = { 'Mark' },
		['greek'] = { 'Κατά Μάρκον' },
		['latin'] = { 'Marcus' },
		['div'] = '章',
	},
	['Lk'] = {
		['order'] = 620,
		['protestant'] = { '路加福音', '路' },
		['catholic'] = { '路加福音', '路', '{{full}}聖路加福音' },
		['orthodox'] = { '聖福音依路喀所傳者' },
		['english'] = { 'Luke' },
		['greek'] = { 'Κατά Λουκάν' },
		['latin'] = { 'Lucas' },
		['div'] = '章',
	},
	['Jn'] = {
		['order'] = 630,
		['protestant'] = { '約翰福音', '約' },
		['catholic'] = { '若望福音', '若', '{{full}}聖若望福音' },
		['orthodox'] = { '聖福音依約安所傳者' },
		['english'] = { 'John' },
		['greek'] = { 'Κατά Ιωάννην' },
		['latin'] = { 'Ioannes' },
		['div'] = '章',
	},
	['Ac'] = {
		['order'] = 640,
		['protestant'] = { '使徒行傳', '徒' },
		['catholic'] = { '宗徒大事錄', '宗' },
		['orthodox'] = { '使徒行實' },
		['english'] = { 'Acts' },
		['greek'] = { 'Πράξεις' },
		['latin'] = { 'Actus Apostolorum' },
		['div'] = '章',
	},
	['Rm'] = {
		['order'] = 650,
		['protestant'] = { '羅馬書', '羅' },
		['catholic'] = { '羅馬書', '羅', '{{full}}聖保祿宗徒致羅馬人書' },
		['orthodox'] = { '致羅馬人書' },
		['english'] = { 'Romans', 'Rom.' },
		['greek'] = { 'Προς Ρωμαίους' },
		['latin'] = { 'Ad Romanos' },
		['div'] = '章',
	},
	['1Co'] = {
		['order'] = 660,
		['protestant'] = { '哥林多前書', '林前' },
		['catholic'] = { '格林多前書', '格前', '{{full}}聖保祿宗徒致格林多人前書' },
		['orthodox'] = { '致科林托人書一' },
		['english'] = { '1 Corinthians', '1 Cor.' },
		['greek'] = { 'Προς Κορινθίους Αʹ' },	-- U+0374 Greek numeral sign
		['latin'] = { 'Ad Corinthios I' },
		['div'] = '章',
	},
	['2Co'] = {
		['order'] = 670,
		['protestant'] = { '哥林多後書', '林後' },
		['catholic'] = { '格林多後書', '格後', '{{full}}聖保祿宗徒致格林多人後書' },
		['orthodox'] = { '致科林托人書二' },
		['english'] = { '2 Corinthians', '2 Cor.' },
		['greek'] = { 'Προς Κορινθίους Βʹ' },	-- U+0374 Greek numeral sign
		['latin'] = { 'Ad Corinthios II' },
		['div'] = '章',
	},
	['Gl'] = {
		['order'] = 680,
		['protestant'] = { '加拉太書', '加' },
		['catholic'] = { '迦拉達書', '迦', '{{full}}聖保祿宗徒致迦拉達人書' },
		['orthodox'] = { '致噶拉塔人書' },
		['english'] = { 'Galatians', 'Gal.' },
		['greek'] = { 'Προς Γαλάτας' },
		['latin'] = { 'Ad Galatas' },
		['div'] = '章',
	},
	['Eph'] = {
		['order'] = 690,
		['protestant'] = { '以弗所書', '弗' },
		['catholic'] = { '厄弗所書', '弗', '{{full}}聖保祿宗徒致厄弗所人書' },
		['orthodox'] = { '致艾弗所人書' },
		['english'] = { 'Ephesians', 'Eph.' },
		['greek'] = { 'Προς Εφεσίους' },
		['latin'] = { 'Ad Ephesios' },
		['div'] = '章',
	},
	['Php'] = {
		['order'] = 700,
		['protestant'] = { '腓立比書', '腓' },
		['catholic'] = { '斐理伯書', '斐', '{{full}}聖保祿宗徒致斐理伯人書' },
		['orthodox'] = { '致斐利彼人書' },
		['english'] = { 'Philippians', 'Phil.' },
		['greek'] = { 'Προς Φιλιππησίους' },
		['latin'] = { 'Ad Philippenses' },
		['div'] = '章',
	},
	['Col'] = {
		['order'] = 710,
		['protestant'] = { '歌羅西書', '西' },
		['catholic'] = { '哥羅森書', '哥', '{{full}}聖保祿宗徒致哥羅森人書' },
		['orthodox'] = { '致科羅西人書' },
		['english'] = { 'Colossians' },
		['greek'] = { 'Προς Κολοσσαείς' },
		['latin'] = { 'Ad Colossenses' },
		['div'] = '章',
	},
	['1Th'] = {
		['order'] = 720,
		['protestant'] = { '帖撒羅尼迦前書', '帖前' },
		['catholic'] = { '得撒洛尼前書', '得前', '{{full}}聖保祿宗徒致得撒洛尼人前書' },
		['orthodox'] = { '致德撒洛尼基人書一' },
		['english'] = { '1 Thessalonians', '1 Thes.' },
		['greek'] = { 'Προς Θεσσαλονικείς Αʹ' },	-- U+374
		['latin'] = { 'Ad Thessalonicenses I' },
		['div'] = '章',
	},
	['2Th'] = {
		['order'] = 730,
		['protestant'] = { '帖撒羅尼迦後書', '帖後' },
		['catholic'] = { '得撒洛尼後書', '得後', '{{full}}聖保祿宗徒致得撒洛尼人後書' },
		['orthodox'] = { '致德撒洛尼基人書二' },
		['english'] = { '2 Thessalonians', '2 Thes.' },
		['greek'] = { 'Προς Θεσσαλονικείς Βʹ' },	-- U+374
		['latin'] = { 'Ad Thessalonicenses II' },
		['div'] = '章',
	},
	['1Tm'] = {
		['order'] = 740,
		['protestant'] = { '提摩太前書', '提前' },
		['catholic'] = { '弟茂德前書', '弟前', '{{full}}聖保祿宗徒致弟茂德前書' },
		['orthodox'] = { '致提摩泰書一' },
		['english'] = { '1 Timothy' },
		['greek'] = { 'Προς Τιμόθεον Αʹ' },	-- U+374
		['latin'] = { 'Ad Timotheum I' },
		['div'] = '章',
	},
	['2Tm'] = {
		['order'] = 750,
		['protestant'] = { '提摩太後書', '提後' },
		['catholic'] = { '弟茂德後書', '弟後', '{{full}}聖保祿宗徒致弟茂德後書' },
		['orthodox'] = { '致提摩泰書二' },
		['english'] = { '2 Timothy' },
		['greek'] = { 'Προς Τιμόθεον Βʹ' },	-- U+374
		['latin'] = { 'Ad Timotheum II' },
		['div'] = '章',
	},
	['Ti'] = {
		['order'] = 760,
		['protestant'] = { '提多書', '多' },
		['catholic'] = { '弟鐸書', '鐸', '{{full}}聖保祿宗徒致弟鐸書' },
		['orthodox'] = { '致提托書' },
		['english'] = { 'Titus' },
		['greek'] = { 'Προς Τίτον' },
		['latin'] = { 'Ad Titum' },
		['div'] = '章',
	},
	['Phm'] = {
		['order'] = 770,
		['protestant'] = { '腓利門書', '門' },
		['catholic'] = { '費肋孟書', '費', '{{full}}聖保祿宗徒致費肋孟書' },
		['orthodox'] = { '致斐利蒙書' },
		['english'] = { 'Philemon', 'Phlm.' },
		['greek'] = { 'Προς Φιλήμονα' },
		['latin'] = { 'Ad Philemonem' },
		['div'] = '節',
	},
	['Heb'] = {
		['order'] = 780,
		['protestant'] = { '希伯來書', '來' },
		['catholic'] = { '希伯來書', '希', '{{full}}致希伯來人書' },
		['orthodox'] = { '致希伯來人書' },
		['english'] = { 'Hebrews', 'Phlm.' },
		['greek'] = { 'Προς Εβραίους' },
		['latin'] = { 'Ad Hebraeos' },
		['div'] = '章',
	},
	['Jms'] = {
		['order'] = 790,
		['protestant'] = { '雅各書', '雅' },
		['catholic'] = { '雅各伯書', '雅' }, -- not sure what the long names of these ones are
		['orthodox'] = { '雅科弗書信' },
		['english'] = { 'James', 'Jas.' },
		['greek'] = { 'Ιακώβου' },
		['latin'] = { 'Iacobi' },
		['div'] = '章',
	},
	['1Pt'] = {
		['order'] = 800,
		['protestant'] = { '彼得前書', '彼前' },
		['catholic'] = { '伯多祿前書', '伯前' },
		['orthodox'] = { '裴特若書信一' },
		['english'] = { '1 Peter' },
		['greek'] = { 'Πέτρου Αʹ' },	-- U+374
		['latin'] = { 'Petri I' },
		['div'] = '章',
	},
	['2Pt'] = {
		['order'] = 810,
		['protestant'] = { '彼得後書', '彼後' },
		['catholic'] = { '伯多祿後書', '伯後' },
		['orthodox'] = { '裴特若書信二' },
		['english'] = { '1 Peter' },
		['greek'] = { 'Πέτρου Βʹ' },	-- U+374
		['latin'] = { 'Petri II' },
		['div'] = '章',
	},
	['1Jn'] = {
		['order'] = 820,
		['protestant'] = { '約翰一書', '約壹' },
		['catholic'] = { '若望一書', '若一', '若壹', '壹若' },
		['orthodox'] = { '約安書信一' },
		['english'] = { '1 John' },
		['greek'] = { 'Ιωάννου Αʹ' },	-- U+374
		['latin'] = { 'Ioannis I' },
		['div'] = '章',
	},
	['2Jn'] = {
		['order'] = 830,
		['protestant'] = { '約翰二書', '約貳' },
		['catholic'] = { '若望二書', '若二', '若貳', '貳若' },
		['orthodox'] = { '約安書信二' },
		['english'] = { '2 John' },
		['greek'] = { 'Ιωάννου Βʹ' },	-- U+374
		['latin'] = { 'Ioannis II' },
		['div'] = '節',
	},
	['3Jn'] = {
		['order'] = 840,
		['protestant'] = { '約翰三書', '約叁' },
		['catholic'] = { '若望三書', '若三', '若叁', '叁若' },
		['orthodox'] = { '約安書信三' },
		['english'] = { '3 John' },
		['greek'] = { 'Ιωάννου Γʹ' },	-- U+374
		['latin'] = { 'Ioannis III' },
		['div'] = '節',
	},
	['Jd'] = {
		['order'] = 850,
		['protestant'] = { '猶大書', '猶' },
		['catholic'] = { '猶達書', '猶' },
		['orthodox'] = { '儒達書信' },
		['english'] = { 'Jude' },
		['greek'] = { 'Ιούδα' },
		['latin'] = { 'Iudae' },
		['div'] = '節',
	},
	['Rv'] = {
		['order'] = 860,
		['protestant'] = { '啟示錄', '啟' },
		['catholic'] = { '{{full}}若望默示錄', '默', '{{preferred}}默示錄' },
		['orthodox'] = { '約安之啟示錄' },
		['english'] = { 'Revelation' },
		['greek'] = { 'Αποκάλυψις Ιωάννου' },
		['latin'] = { 'Apocalypsis' },
		['div'] = '章',
	},
}
local number_of_chapters = {
	['CUV'] = {
		['Gn'] = 50,
		['Ex'] = 40,
		['Lv'] = 27,
		['Nm'] = 36,
		['Dt'] = 34,
		['Jos'] = 24,
		['Jdg'] = 21,
		['Ru'] = 4,
		['1Sm'] = 31,
		['2Sm'] = 24,
		['1Kg'] = 22,
		['2Kg'] = 25,
		['1Ch'] = 29,
		['2Ch'] = 36,
		['Ezr'] = 10,
		['Neh'] = 13,
		['Est'] = 10,
		['Jb'] = 42,
		['Ps'] = 150,
		['Pr'] = 31,
		['Ec'] = 12,
		['Sg'] = 8,
		['Is'] = 66,
		['Jr'] = 52,
		['Lm'] = 5,
		['Ezk'] = 48,
		['Dn'] = 12,
		['Hs'] = 14,
		['Jl'] = 3,		-- Protestant 3 = Catholic 4
		['Am'] = 9,
		['Ob'] = 1,
		['Jnh'] = 4,
		['Mc'] = 7,
		['Nah'] = 3,
		['Hab'] = 3,
		['Zph'] = 3,
		['Hg'] = 2,
		['Zch'] = 14,
		['Mal'] = 4,	-- Protestant 4:1 = Catholic 3:19; a chapter break is missing but verses are identical
		['Mt'] = 28,
		['Mk'] = 16,
		['Lk'] = 24,
		['Jn'] = 21,
		['Ac'] = 28,
		['Rm'] = 16,
		['1Co'] = 16,
		['2Co'] = 13,
		['Gl'] = 6,
		['Eph'] = 6,
		['Php'] = 4,
		['Col'] = 4,
		['1Th'] = 5,
		['2Th'] = 3,
		['1Tm'] = 6,
		['2Tm'] = 4,
		['Ti'] = 3,
		['Phm'] = 1,
		['Heb'] = 13,
		['Jms'] = 5,
		['1Pt'] = 5,
		['2Pt'] = 3,
		['1Jn'] = 5,
		['2Jn'] = 1,
		['3Jn'] = 1,
		['Jd'] = 1,
		['Rv'] = 22,
	};
	['SBV'] = {
		['Gn'] = 50,
		['Ex'] = 40,
		['Lv'] = 27,
		['Nm'] = 36,
		['Dt'] = 34,
		['Jos'] = 24,
		['Jdg'] = 21,
		['Ru'] = 4,
		['1Sm'] = 31,
		['2Sm'] = 24,
		['1Kg'] = 22,
		['2Kg'] = 25,
		['1Ch'] = 29,
		['2Ch'] = 36,
		['Ezr'] = 10,
		['Neh'] = 13,
		['Tb'] = 14,
		['Jdt'] = 16,
		['Est'] = 10 + 6,	-- additional chapters styled as addendums, with separate numbering (A 1 2 3 B 4 C D 5 6 7 8 E 8 9 10 F)
		['1Mc'] = 16,
		['2Mc'] = 15,
		['Jb'] = 42,
		['Ps'] = 150,
		['Pr'] = 31,
		['Ec'] = 12,
		['Sg'] = 8,
		['Wis'] = 19,
		['Sir'] = 51,
		['Is'] = 66,
		['Jr'] = 52,
		['Lm'] = 5,
		['Bar'] = 6,
		['Ezk'] = 48,
		['Dn'] = 14,	-- 13-14 labelled as appendices
		['Hs'] = 14,
		['Jl'] = 4,		-- Catholic 4 = Protestant 3; Catholic 3 is extra
		['Am'] = 9,
		['Ob'] = 1,
		['Jnh'] = 4,
		['Mc'] = 7,
		['Nah'] = 3,
		['Hab'] = 3,
		['Zph'] = 3,
		['Hg'] = 2,
		['Zch'] = 14,
		['Mal'] = 3,	-- Catholic 3:19 = Protestant 4:1; a chapter break is missing but verses are identical
		['Mt'] = 28,
		['Mk'] = 16,
		['Lk'] = 24,
		['Jn'] = 21,	-- 21 labelled as an appendix
		['Ac'] = 28,
		['Rm'] = 16,
		['1Co'] = 16,
		['2Co'] = 13,
		['Gl'] = 6,
		['Eph'] = 6,
		['Php'] = 4,
		['Col'] = 4,
		['1Th'] = 5,
		['2Th'] = 3,
		['1Tm'] = 6,
		['2Tm'] = 4,
		['Ti'] = 3,
		['Phm'] = 1,
		['Heb'] = 13,
		['Jms'] = 5,
		['1Pt'] = 5,
		['2Pt'] = 3,
		['1Jn'] = 5,
		['2Jn'] = 1,
		['3Jn'] = 1,
		['Jd'] = 1,
		['Rv'] = 22,
	};
}
local seq = {
	TORAH = {
		'Gn', 'Ex', 'Lv', 'Nm', 'Dt',
	};
	PROPHETS = {
		'Jos', 'Jdg', 'x-Sm', 'x-Kg',
		'Is', 'Jr', 'Ezk',
		'Hs', 'Jl', 'Am', 'Ob', 'Jnh', 'Mc', 'Nah', 'Hab', 'Zph', 'Hg', 'Zch', 'Mal',
	};
	WRITINGS = {
		'x-Ch', 'Ps', 'Jb', 'Pr', 'Ru', 'Sg', 'Ec', 'Lm', 'Est', 'Dn', 'x-Ezr-Neh',
	};
	CATHOLIC_OLD_TESTAMENT = {
		'Gn', 'Ex', 'Lv', 'Nm', 'Dt',
		'Jos', 'Jdg', 'Ru', '1Sm', '2Sm', '1Kg', '2Kg', '1Ch', '2Ch', 'Ezr', 'Neh',
		'Tb', 'Jdt', 'Est', '1Mc', '2Mc',
		'Jb', 'Ps', 'Pr', 'Ec', 'Sg', 'Wis', 'Sir',
		'Is', 'Jr', 'Lm', 'Bar', 'Ezk', 'Dn',
		'Hs', 'Jl', 'Am', 'Ob', 'Jnh', 'Mc', 'Nah', 'Hab', 'Zph', 'Hg', 'Zch', 'Mal',
	};
	PROTESTANT_OLD_TESTAMENT = {
		'Gn', 'Ex', 'Lv', 'Nm', 'Dt',
		'Jos', 'Jdg', 'Ru', '1Sm', '2Sm', '1Kg', '2Kg', '1Ch', '2Ch', 'Ezr', 'Neh',
		'Est',
		'Jb', 'Ps', 'Pr', 'Ec', 'Sg',
		'Is', 'Jr', 'Lm', 'Ezk', 'Dn',
		'Hs', 'Jl', 'Am', 'Ob', 'Jnh', 'Mc', 'Nah', 'Hab', 'Zph', 'Hg', 'Zch', 'Mal',
	};
	NEW_TESTAMENT = {
		'Mt', 'Mk', 'Lk', 'Jn',
		'Ac',
		'Rm', '1Co', '2Co', 'Gl', 'Eph', 'Php', 'Col', '1Th', '2Th', '1Tm', '2Tm', 'Ti', 'Phm',
		'Heb', 'Jms', '1Pt', '2Pt', '1Jn', '2Jn', '3Jn', 'Jd',
		'Rv',
	}
}
local parts = {
	CHRISTIAN_NEW_TESTAMENT = {
		{
			['names'] = { '新約' };
			['books'] = seq.NEW_TESTAMENT;
		},
	};
	CATHOLIC = {
		{
			['names'] = { '舊約' };
			['books'] = seq.CATHOLIC_OLD_TESTAMENT;
		}, {
			['names'] = { '新約' };
			['books'] = seq.NEW_TESTAMENT;
		},
	};
	PROTESTANT = {
		{
			['names'] = { '舊約' };
			['books'] = seq.PROTESTANT_OLD_TESTAMENT;
		}, {
			['names'] = { '新約' };
			['books'] = seq.NEW_TESTAMENT;
		},
	};
	JUDAISM = {
		{
			['names'] = { '五經' };
			['books'] = seq.TORAH;
		}, {
			['names'] = { '先知' };
			['books'] = seq.PROPHETS;
		}, {
			['names'] = { '聖錄' };
			['books'] = seq.WRITINGS;
		},
	};
}
local versions = {
	['CSB'] = {				-- for testing
		['tradition'] = 'protestant';
		['language'] = languages.ENGLISH;
		['number_of_chapters'] = number_of_chapters.CUV;
		['parts'] = parts.PROTESTANT;
	};
	['CNV'] = {
		['names'] = {
			'新譯本',
			'聖經新譯本',
		};
		['link'] = '聖經新譯本',
		['tradition'] = 'protestant';
		['number_of_chapters'] = number_of_chapters.CUV;
		['parts'] = parts.PROTESTANT;
		['url'] = 'https://www.biblegateway.com/passage/?search=&book;+&numbers;&version=CNVT';
	};
	['CUV'] = {
		['names'] = {
			'和合本',
			'聖經和合本',
		};
		['link'] = '聖經和合本';
		['tradition'] = 'protestant';
		['number_of_chapters'] = number_of_chapters.CUV;
		['parts'] = parts.PROTESTANT;
		['url'] = 'https://www.biblegateway.com/passage/?search=&book;+&numbers;&version=CUVMPT';
	};
	['JPS'] = {				-- for testing
		['link'] = '塔納赫#英文版《塔納赫》';
		['tradition'] = 'judaism';
		['language'] = languages.ENGLISH;
		['number_of_chapters'] = nil;
		['parts'] = parts.JUDAISM;
		['url'] = 'https://mechon-mamre.org/p/pt/pt&book;&chapter;.htm';
		['url_book_transform'] = function (s, id)
			local codes = {
					['Gn'] = '01',
					['Ex'] = '02',
					['Lv'] = '03',
					['Nm'] = '04',
					['Dt'] = '05',
					['Jos'] = '06',
					['Jdg'] = '07',
					['Ru'] = '29',
					['1Sm'] = '08a',
					['2Sm'] = '08b',
					['1Kg'] = '09a',
					['2Kg'] = '09b',
					['1Ch'] = '25a',
					['2Ch'] = '25b',
					['Ezr'] = '35a',
					['Neh'] = '35b',
					['Est'] = '33',
					['Jb'] = '27',
					['Ps'] = '26',
					['Pr'] = '28',
					['Ec'] = '31',
					['Sg'] = '30',
					['Is'] = '10',
					['Jr'] = '11',
					['Lm'] = '32',
					['Ezk'] = '12',
					['Dn'] = '34',
					['Hs'] = '13',
					['Jl'] = '14',
					['Am'] = '15',
					['Ob'] = '16',
					['Jnh'] = '17',
					['Mc'] = '18',
					['Nah'] = '19',
					['Hab'] = '20',
					['Zph'] = '21',
					['Hg'] = '22',
					['Zch'] = '23',
					['Mal'] = '24',
				}
				if not codes[id] then
					error('Internal error: book code "' .. id .. '" (name ' .. s .. ') not found in JPS codes table');
				end
				return codes[id];
			end;
		['url_chapter_transform'] = function (s)
				return string.format('%x', math.floor(s/10)) .. string.format('%d', s%10);
			end;
	};
	['KJV'] = {
		['names'] = {
			'欽定本',
			'英王欽定本',
		};
		['link'] = '英王欽定本',
		['tradition'] = 'protestant';
		['number_of_chapters'] = number_of_chapters.CUV;
		['parts'] = parts.PROTESTANT;
		['url'] = 'https://www.biblegateway.com/passage/?search=&book;+&numbers;&version=KJV';
	};
	['NAB'] = {
		['tradition'] = 'catholic';
		['language'] = languages.ENGLISH;
		['number_of_chapters'] = nil;
		['parts'] = parts.CATHOLIC;
		['has_embeds_p'] = true;
		['url'] = 'https://bible.usccb.org/bible/&book;/&chapter;';
		['url_book_transform'] = function (s)
				return s:lower():gsub('%+', '');
			end;
	};
	['NIV'] = {
		['tradition'] = 'protestant';
		['language'] = languages.ENGLISH;
		['number_of_chapters'] = nil;
		['parts'] = parts.PROTESTANT;
		['url'] = 'https://www.biblegateway.com/passage/?search=&book;+&numbers;&version=NIV';
	};
	['NRSVA'] = {
		['names'] = {
			'NRSV 英國英文版',
		};
		['tradition'] = 'protestant';
		['language'] = languages.ENGLISH;
		['number_of_chapters'] = nil;
		['parts'] = parts.PROTESTANT;
		['url'] = 'https://www.biblegateway.com/passage/?search=&book;+&numbers;&version=NRSVA';
	};
	['SBV'] = {
		['names'] = {
			'思高本',
			'思高聖經',
			'思高中文聖經',
			'聖經思高本',
		};
		['link'] = '聖經思高本';
		['tradition'] = 'catholic';
		['number_of_chapters'] = number_of_chapters.SBV;
		['parts'] = parts.CATHOLIC;
		['has_embeds_p'] = true;
		['url'] = 'https://www.ccreadbible.org/Chinese%20Bible/sigao/&book;_bible_Ch_&chapter;_.html';
		['url_book_transform'] = function (s)
				return s:gsub('%+', '_');
			end;
	};
	['Vulgate'] = {
		['names'] = {
			'拉丁通用本',
			'拉丁通俗本',
			'拉丁普及本',
			'聖經拉丁通用本',
			'聖經拉丁通俗本',
			'聖經拉丁普及本',
			'武加大譯本',
		};
		['link'] = '聖經拉丁通用本';
		['tradition'] = 'latin';
		['number_of_chapters'] = number_of_chapters.SBV;
		['parts'] = parts.CATHOLIC;
		['has_embeds_p'] = true;
		['url'] = 'https://www.biblegateway.com/passage/?search=&book;+&numbers;&version=Vulgate';
	};
	['SBLGNT'] = {	-- https://sblgnt.com/ (CC 4.0)
		['names'] = {
			'SBL 希臘文新約',
		};
		['tradition'] = 'greek';
		['number_of_chapters'] = nil;
		['parts'] = parts.CHRISTIAN_NEW_TESTAMENT;
		['url'] = 'https://www.biblegateway.com/passage/?search=&book;+&numbers;&version=SBLGNT';
	};
	['THGNT'] = {	-- https://academic.tyndalehouse.com/research/the-greek-new-testament/
		['names'] = {
			'Tyndale House 希臘文新約',
		};
		['tradition'] = 'greek';
		['number_of_chapters'] = nil;
		['parts'] = parts.CHRISTIAN_NEW_TESTAMENT;
		['url'] = 'https://www.biblegateway.com/passage/?search=&book;+&numbers;&version=THGNT';
	};
	['TR1550'] = {
		['names'] = {
			'公認經文',	-- Protestant name
			'TR',
		};
		['tradition'] = 'greek';
		['number_of_chapters'] = nil;
		['parts'] = parts.CHRISTIAN_NEW_TESTAMENT;
		['url'] = 'https://www.biblegateway.com/passage/?search=&book;+&numbers;&version=TR1550';
	};
	['TR1894'] = {
		['names'] = {
			'公認經文',	-- Protestant name
			'TR',
		};
		['tradition'] = 'greek';
		['number_of_chapters'] = nil;
		['parts'] = parts.CHRISTIAN_NEW_TESTAMENT;
		['url'] = 'https://www.biblegateway.com/passage/?search=&book;+&numbers;&version=TR1894';
	};
	['WH'] = {
		['names'] = {
			'魏斯科-霍特新約希臘文聖經',	-- Lo et al. (2003) p. 580, de-Mandarinized
			'WHNU',
		};
		['tradition'] = 'greek';
		['number_of_chapters'] = nil;
		['parts'] = parts.CHRISTIAN_NEW_TESTAMENT;
		['url'] = 'https://www.biblegateway.com/passage/?search=&book;+&numbers;&version=WHNU';
	};
}

local pats = {
	['key val'] = '^([1-4]? *[^0-9]+) *(.*)$',
	['key add vv'] = '^([1-4]? *[^0-9]+) *([A-Z]:.*)$',
	['n'] = '^(([1-9][0-9]*))$',
	['A'] = '^(([A-J]))$',
	['n:n'] = '^(([1-9][0-9]*):([1-9][0-9]*))$',
	['A:n'] = '^(([A-J]):([1-9][0-9]*))$',
	['n:n-n'] = '^(([1-9][0-9]*):([1-9][0-9]*-[1-9][0-9]*))$',
	['A:n-n'] = '^(([A-J]):([1-9][0-9]*-[1-9][0-9]*))$',
}

--- Auxiliaries ---------------------------------------------------------------

local function fix_argument (s)
	return s:gsub('^ +', ''):gsub(' +$', '');
end

local function croak (s)
	local frame = mw.getCurrentFrame();
	if frame:getParent() then
		frame = frame:getParent();
	end
	local note = '(引模參數係:' .. Syu1meng2.cvs(frame.args) .. ')';
	error('引聖經失敗,' .. s .. note, 0);
end

local function parse_tagged_name (s)
	local it = { tags = {} };
	while true do
		local tag, remainder = mw.ustring.match(s, '^\{\{([^\{\}]*)\}\}(.*)$');
	if remainder == nil then break end;
		s = remainder;
	if not tag then break end;
		it.tags[tag] = true;
	end
	it.value = s;
	return it;
end

local function get_book_definition (id, tradition)
	local it;
	if tradition == 'judaism' and books[id] and not books[id][tradition] then
		tradition = 'hebrew';	-- XXX stupid hack for testing
	end
	if books[id] and books[id][tradition] then
		it = books[id][tradition];
	end
	return it;
end

local function book_exists_in_tradition (id, tradition)
	return get_book_definition(id, tradition);
end

local function find_book_name (id, tradition, form)
	local it;
	if tradition and get_book_definition(id, tradition) then
		-- start from position 1 and look for a name with the correct tag
		local candidates = get_book_definition(id, tradition);
		local fallback;
		for i = 1, #candidates do
			local candidate = parse_tagged_name(candidates[i]);
			if candidate.tags[form] then
				it = candidate.value;
			elseif not fallback then
				fallback = candidate.value;
			end
		if fallback and not form then break end;
		end
		if not it then
			it = fallback;
		end
	elseif tradition then
		error('Internal error, book ' .. id .. ' does not exist in tradition ' .. tradition
				.. ' (correct language label is missing in books array)', 2);
	else
		error('Internal error, invalid book id ' .. id, 2);
	end
	return it;
end

local function describe_tradition_genitive (tradition)
	local it;
	if not traditions[tradition] then
		it = tradition .. ' ';
	elseif tradition == 'english' then		-- XXX stupid hack
		it = '其他教派講';
	else
		it = traditions[tradition];
	end
	if #it > 0 then
		it = it .. '嘅';
	end
	return it;
end

local function describe_book (id, tradition, describe_tradition_p)
	local it;
	if describe_tradition_p == nil then
		describe_tradition_p = true;
	end
	if tradition and get_book_definition(id, tradition) then
		it = '';
		if describe_tradition_p then
			it = it .. describe_tradition_genitive(tradition);
		end
		it = it .. '《' .. find_book_name(id, tradition, nil) .. '》';
	else
		for _, candidate_tradition in pairs({'protestant', 'catholic', 'orthodox', 'judaism'}) do
			if get_book_definition(id, candidate_tradition) then
				it = '';
				if describe_tradition_p then
					it = it .. describe_tradition_genitive(candidate_tradition);
				end
				it = it .. '《' .. find_book_name(id, candidate_tradition, nil) .. '》';
			end
		if it then break end;
		end
		if not it then
			error('Internal error, got invalid book id ' .. id, 2);
		end
	end
	return it;
end

local function canonicalize_version (id_or_name)
	local it = id_or_name;
	if not versions[id_or_name] then
		local candidates = {};
		for id, version in pairs(versions) do
			if version.names then
				local something_done_p = false;
				for _, candidate in pairs(version.names) do
					if id_or_name == candidate then
						table.insert(candidates, id);
						something_done_p = true;
					end
				if something_done_p then break end;
				end
			end
		end
		if #candidates > 1 then
			croak('「' .. id_or_name .. '」 可以指:' .. mw.text.listToText(candidates, '、', ' 或者 '));
		elseif #candidates == 1 then
			it = candidates[1];
		end
	end
	return it;
end

local function describe_version (id, suppress_spacing_tweaks_p)
	local it;
	if versions[id] and versions[id].names then
		it = versions[id].names[1];
	else
		it = id;
		if id:match('[a-zA-Z]$') then
			if not suppress_spacing_tweaks_p then
				it = it .. ' ';
			end
			it = it .. '譯本';
		end
	end
	return it;
end

local function describe_ambiguity (ids, conjunction, describe_tradition_p)
	local it = '';
	if not conjunction then
		conjunction = '或者';
	end
	if describe_tradition_p == nil then
		describe_tradition_p = true;
	end
	if false then		-- if debugging
		it = Syu1meng2.cvs(ids)
	else
		for i = 1, #ids do
			if i == 1 then
				-- I hate Lua
			elseif i == #ids then
				it = it .. ' ' .. conjunction;
			elseif i > 1 then
				it = it .. '、';
			end
			it = it .. describe_book(ids[i].id, ids[i].naming, describe_tradition_p);
			it = it .. '(' .. ids[i].id .. ')';
		end
	end
	return it;
end		

local function find_book (s)
	local it = {};
	if books[s] then
		table.insert(it, {['id'] = s; });
	else
		local s1 = mw.ustring.upper(mw.ustring.gsub(s, '[ %.]', ''));
		for key, book in pairs(books) do
			for _, naming in pairs({'protestant', 'catholic', 'orthodox', 'english'}) do
				if book[naming] then
					for j, candidate_with_tags in pairs(book[naming]) do
						local candidate = (parse_tagged_name(candidate_with_tags))['value'];
						if candidate == s then
							table.insert(it, {['id'] = key; ['naming'] = naming..''; });
						elseif naming == 'english' and mw.ustring.upper(mw.ustring.gsub(candidate, '[ %.]', '')) == s1 then
							table.insert(it, {['id'] = key; });
						end
					end
				end
			end
		end
	end
	return it;
end

local function assert_canonical_book_code (generic)
	local candidates = find_book(generic);
	local it;
	if #candidates == 0 then
		croak('搵唔到叫 「' .. generic .. '」 嘅書卷');
	elseif #candidates > 1 then
		croak('「' .. generic .. '」 可以指' .. describe_ambiguity(candidates, '或者'));
	else
		it = candidates[1].id;
	end
	return it;
end

local function parse_reference (s)
	local it;
	local key, specific, chapter, verses;
	if type(s) == 'table' then
		key = assert_canonical_book_code(s.book);
		specific = s.numbers;
		chapter = s.chapter;
		verses = s.verses;
		it = s;
	else
		-- Split book and numbers; try the weird ones first (only in catholic)
		local generic;
		generic, specific = mw.ustring.match(s, pats['key add vv']);
		if not specific then
			generic, specific = mw.ustring.match(s, pats['key val']);
		end
		if generic then
			generic = mw.ustring.gsub(generic, ' +$', '');
			key = assert_canonical_book_code(generic);
			it = {};
		end
	end
	if specific and not (chapter or verses) then
		local numbers;
		for _, pat in pairs({'n', 'A'}) do
			numbers, chapter = mw.ustring.match(specific, pats[pat]);
		if numbers then break end;
		end
		if not numbers then
			for _, pat in pairs({'n:n', 'A:n', 'n:n-n', 'A:n-n'}) do
				numbers, chapter, verses = mw.ustring.match(specific, pats[pat]);
			if numbers then break end;
			end
		end
		if not numbers then
			croak('唔明 「' .. specific .. '」 係乜嘢章節');
		end
	end
	if key then
		if books[key].div == '節' then
			verses = chapter;
			chapter = nil;
		end
		it.book = key;
		it.chapter = chapter;
		it.verses = verses;
	end
	return it;
end

local function find_abbreviation (id, tradition, version, allow_informal_digits_p, allow_fallback_p)
	local it;
	local specified_language;
	if tradition == 'english' then
		specified_language = tradition;
	elseif version and versions[version] and versions[version].language then
		specified_language = versions[version].language;
	end
	if specified_language then
		if specified_language == languages.ENGLISH then
			-- FIXME. not ideal for tradition = 'judaism', outright wrong for tradition = 'orthodox'
			it = id;
		else
			tradition = language;	-- FIXME temporary hack
		end
	end
	if not it then
		local candidates = get_book_definition(id, tradition);
		if candidates and #candidates > 1 then
			-- Start from position 2 and look for what looks like a short form
			if allow_informal_digits_p == nil then
				allow_informal_digits_p = not numbering[tradition].kanji_chapters_p;
			end
			for i = 2, #candidates do
				local candidate = candidates[i];
				if mw.ustring.match(candidate, '^..?$') and (
						allow_informal_digits_p
					or mw.ustring.match(candidate, '[^一二三四]$')) then

					it = candidate;
				end
			if it then break end;
			end
		end
		if not it and allow_fallback_p then		-- no abbreviated forms found
			it = candidates[1];
		end
	end
	return it;
end

local function format_numbering (ref, tradition, version)
	local it;
	local book = books[ref.book];
	local embeds = book.embeds;
	local error_p = false;
	if tradition == nil then
		it = '';
		if book.div == '節' then
			it = it .. ref.verses .. book.div;
		elseif ref.chapter then
			if stems[ref.chapter] then
				it = it .. '補錄' .. stems[ref.chapter];	-- FIXME
			else
				it = it .. ref.chapter .. book.div;
			end
			if ref.verses then
				it = it .. ref.verses .. '節';
			end
		end
	else
		local t = find_abbreviation(ref.book, tradition, version, nil, nil);
		if not t then
			t = find_book_name(ref.book, tradition, nil);
		end
		if not t then
			error('format_numbering: Internal error, tradition “' .. tradition .. '” not defined in book definition '..Syu1meng2.cvs(book), 2);
		end
		local book = mw.ustring.gsub(book.english[1], '%s+', '+');
		if Syu1meng2.cjk_p(t) then
			t = Syu1meng2.auto_build_citable(nil, t);
		else
			t = t .. ' ';
		end
		local numbers = '';
		-- Note that ref.chapter may not exist; some books don't have chapters
		if ref.chapter and ref.verses then
			numbers = ref.chapter .. ':' .. ref.verses;
		elseif ref.chapter then
			numbers = ref.chapter;
		else
			numbers = ref.verses;
		end
		if numbering[tradition].kanji_chapters_p then
			if ref.chapter then
				if stems[ref.chapter] then
					t = t .. '補錄' .. stems[ref.chapter]	-- FIXME
				else
					t = t .. NumberToChinese._numberToChinese(ref.chapter)
				end
			end
			if ref.verses then
				t = t .. ref.verses;
			end
		elseif numbers then
			t = t .. numbers;
		end
		local unknown_version_p = false;
		if not version then
			version = numbering[tradition].default;
		end
		if version and not versions[version] then
			--croak('唔知 「' .. version .. '」 係乜嘢譯本');
			unknown_version_p = true;
		elseif version and tradition and versions[version] and versions[version].tradition ~= tradition then
			croak('「' .. version .. '」 唔係' .. traditions[tradition] .. '譯本');
		end
		if version and versions[version] and versions[version].url then
			-- Construct an external link (CUV works; others, maybe)
			local book_code = book;	-- note that this is not the id, and spaces are already replaced by +
			local chapter_code = ref.chapter;
			local f = versions[version].url_book_transform;
			local g = versions[version].url_chapter_transform;
			if f then
				book_code = f(book_code, ref.book);
			end
			if g then
				chapter_code = g(chapter_code);
			end
			if embeds and embeds[chapter_code] then
				chapter_code = embeds[chapter_code];
			end
			t = '[' .. versions[version].url
								:gsub('&book;', book_code)
								:gsub('&numbers;', numbers or '')
								:gsub('&chapter;', chapter_code or '1')
					.. ' ' .. t .. ']';
		elseif unknown_version_p then
			-- Assume specified version is a Bible Gateway code
			t = '[https://www.biblegateway.com/passage/?search=' .. book .. (numbers or '') .. '&version=' .. version
					.. ' ' .. t .. ']';
		end
		if not version then
			t = '<small>' .. traditions[tradition] .. ':</small>' .. t;
		elseif unknown_version_p then
			t = '<small>不明譯本' .. version .. ':</small>' .. t;
		else
			local label = version;
			if versions[version].names then
				label = versions[version].names[1];
			end
			if versions[version].link then
				label = '[[' .. versions[version].link .. '|' .. label .. ']]';
			end
			t = '<small>' .. traditions[tradition] .. label .. ':</small>' .. t;
		end
		it = t;
	end
	--it = Syu1meng2.cvs(ref);	-- DEBUG
	return it;
end

local function version_has_book_p (version,  id)
	local it = false;
	if versions[version] and versions[version].parts then
		local parts = versions[version].parts;
		for i, part_id in pairs(parts) do
			for j, book_id in pairs(parts[i].books) do
				if book_id == id then
					it = book_id;
				-- Check if the listed book is a theoretical aggregate (e.g., "Kings" or "Chronicles")
				elseif books[book_id].parts then
					for k, scroll_id in pairs(books[book_id].parts) do
						if scroll_id == id then
							it = scroll_id;
						end
					if it then break end;
					end
				end
			if it then break end;
			end
		if it then break end;
		end
	end
	return it;
end

local function version_not_compatible_p (ref, version)
	local it;
	assert(version);
	assert(versions[version]);
	assert(ref.book);

	-- Does the book exist?
	if not version_has_book_p(version, ref.book) then
		it = describe_version(version) .. '冇書卷' .. describe_book(ref.book, nil, false);

	-- Do we require weird alphabetic chapters but the version doesn't have them?
	elseif stems[ref.chapter] and not versions[version].has_embeds_p then
		it = describe_version(version) .. '嘅' .. describe_book(ref.book , nil, false).. '唔會有 「補錄' .. stems[ref.chapter] .. '」';

	end
	return it;
end

local function assert_compatibility (ref, version)
	local det = version_not_compatible_p(ref, version);
	if det then
		croak(det);
	end
end

local function construct_formatted_reference (s)
	local it;
	local ref;
	ref = parse_reference(s);
	if ref then
		local book = books[ref.book];
		local preferred_name, full_name, tradition;
		local require_embedding_p = stems[ref.chapter];
		local candidate_traditions;
		if require_embedding_p then
			candidate_traditions = {'catholic', 'orthodox'};
		else
			candidate_traditions = {'protestant', 'catholic', 'orthodox', 'judaism'};
		end
		it = '';
		for _, candidate_tradition in pairs(candidate_traditions) do
			if book_exists_in_tradition(ref.book, candidate_tradition) then
				tradition = candidate_tradition;
				preferred_name = find_book_name(ref.book, tradition, 'preferred');
				full_name = find_book_name(ref.book, tradition, 'full');
			end
		if full_name then break end;
		end
		-- quick check to see if the citation is clearly impossible
		if book.parts then
			local tmp = {};
			for i = 1, #(book.parts) do
				table.insert(tmp, { ['id'] = book.parts[i], ['naming'] = tradition });
			end
			croak('「' .. preferred_name .. '」 喺' .. traditions[tradition] .. '概念上係一卷書,但係實際引用嗰陣,要指明係' .. describe_ambiguity(tmp, '定係', false));
		elseif require_embedding_p and (not book.embeds or not book.embeds[ref.chapter]) then
			croak('' .. preferred_name .. '似乎冇 「補錄' .. stems[ref.chapter] .. '」');
		end
		-- emit the reference, without any external links
		it = '[[' .. full_name .. '|' .. Syu1meng2.auto_build_citable(nil, preferred_name) .. ']]';
		it = it .. format_numbering(ref, nil, nil);
		-- figure out usable versions then emit them with corresponding links
		local specifics = {};
		local seealso = {};
		if ref.version then
			local v;
			if versions[ref.version] then	-- known translation
				assert_compatibility(ref, ref.version);
				v = format_numbering(ref, versions[ref.version].tradition, ref.version);
				-- Create tracking category, genericizing (de-Protestantizing) the term for "Greek NT"
				local version_label = describe_version(ref.version, true);
				version_label = mw.ustring.gsub(version_label, '新約希臘文聖經', '新約原文版本');	-- rm Lo et al. (2003) transl.
				version_label = mw.ustring.gsub(version_label, '希臘文新約', '新約原文版本');	-- rm my own transl.
				it = it ..  '[[Category:引聖經指定用';
				if version_label:match(' ') then
					it = it .. ' ';
				end
				it = it .. describe_tradition_genitive(versions[ref.version].tradition)
						.. version_label .. '嘅文]]';
			else							-- unknown translation
				it = it ..  '[[Category:引聖經用咗不明譯本嘅文]]';
				it = it ..  '[[Category:引聖經指定用不明譯本' .. ref.version .. '嘅文]]';
				v = format_numbering(ref, 'protestant', ref.version);
			end
			table.insert(specifics, v);
		end
		for _, candidate_tradition in pairs({'protestant', 'catholic'}) do
			if numbering[candidate_tradition] then
				local candidate_version = numbering[candidate_tradition].default;
				if candidate_version ~= ref.version
					and book[candidate_tradition]
					and (not require_embedding_p or versions[candidate_version].has_embeds_p) then

					local v = format_numbering(ref, candidate_tradition, nil);
					assert_compatibility(ref, numbering[candidate_tradition].default);
					table.insert(seealso, v);
				end
			end
		end
		if #specifics > 0 then
			it = it .. '(' ..  table.concat({
					table.concat(specifics, '、'),
					table.concat(seealso, '、') }, ';可以睇埋:') .. ')';
		else
			it = it .. '(' .. table.concat(seealso, '、') .. ')';
		end
	else
		croak('唔明 「' .. s .. '」 係指乜');
	end
	return it;
end

--- Exported, invocable functions ---------------------------------------------

-- Entry point for template
p.cite2 = function( frame )
	return p.cite(frame:getParent());
end

-- Entry point for direct #invoke
p.cite = function( frame )
	local it;
	local styles = '模組:Citation/CS1/styles.css';
	local postscript = '.';
	local ref = {};

	-- Make an attempt to emulate :en:Bibleverse
	-- # doesn't work on frame.args so we have to walk a second pass
	local n = 0;
	local ignore_positional_parameters_p;
	for k, v in pairs(frame.args) do
		if type(k) == 'number' then
			n = n + 1;
		end
	end
	if n > 3 then
		croak('太多唔畀名嘅參數');
	elseif n > 1 then
		ref.book = fix_argument(frame.args[1]);
		ref.numbers = fix_argument(frame.args[2]);
		ignore_positional_parameters_p = true;
		if n == 3 then
			ref.version = fix_argument(frame.args[3]);
		end
	end

	local parts = {};
	it = ''
	for k, v in pairs(frame.args) do
		v = fix_argument(v);
		if type(k) == 'number' then
			if not ignore_positional_parameters_p then
				for k, v in pairs(parse_reference(v)) do
					ref[k] = v;
				end
			end
		elseif k == 'book' or k == 'book-title' or k == 'title' then
			ref.book = v;
		elseif k == 'at' then
			if ref.chapter then
				croak('遇到 「' .. k .. '」 參到,但係已經用咗唔兼容嘅 「chapter」 參數');
			elseif ref.verses then
				croak('遇到 「' .. k .. '」 參到,但係已經用咗唔兼容嘅 「verse」 或者 「verses」 參數');
			elseif ref.numbers then
				croak('之前已經寫咗喺 「' .. ref.numbers .. '」');
			end
			ref.numbers = v;
		elseif k == 'chapter' then
			if ref.numbers then
				croak('遇到 「' .. k .. '」 參到,但係已經用咗唔兼容嘅 「at」 參數');
			elseif ref.chapter then
				croak('之前已經寫咗係第 「' .. ref.chapter .. '」 章');
			end
			ref.chapter = v;
		elseif k == 'postscript' then
			if v == 'none' then
				postscript = '';
			else
				postscript = v;
			end
		elseif k == 'verse' or k == 'verses' then
			if ref.numbers then
				croak('遇到 「' .. k .. '」 參到,但係已經用咗唔兼容嘅 「at」 參數');
			elseif ref.verses then
				croak('之前已經寫咗係第 「' .. ref.verses .. '」 節');
			end
			ref.verses = v;
		elseif k == 'version' then
			if ref.version then
				croak('之前已經寫咗版本係 「' .. ref.version .. '」');
			end
			ref.version = v;
		else
			croak('引聖經模遇到不明參數 「' .. k .. '」');
		end
	end
	if ref.version then
		ref.version = canonicalize_version(ref.version);
	end
	if #parts == 0 then
		if ref.book then
			table.insert(parts, construct_formatted_reference(ref));
		else
			croak('冇畀書卷名');
		end
	end
	if #parts == 0 then
		croak('搵唔到引乜嘢');
	end

	-- build it
	it = table.concat(parts, ';');
	it = it .. postscript;

	-- request our style sheet
	it = table.concat ({
			frame:extensionTag ('templatestyles', '', {src=styles}),
			it
		});
	return it;
end

-- Code dump for documentation page
p.dump_codes = function( frame )
	-- pairs() gives random order, so stash the results in an array first
	local result = {};
	for id, book in pairs(books) do
		local id_sort = id:gsub('^x%-', ''):gsub('^(%d+)(.*)', '%2%1');
		local it = '';
		it = it .. "|-\n";
		it = it .. "|&TK;\n";
		if id:match('^x%-') then
			it = it .. '|data-sort-value="' .. id_sort .. '"|\n';
		else
			it = it .. '|data-sort-value="' .. id_sort .. '"| ' .. id .. "\n";
		end
		for _, k in pairs({'protestant', 'catholic', 'orthodox', 'english'}) do
			if book[k] then
				local book_name = find_book_name(id, k, 'preferred');
				local sort_key = mw.ustring.gsub(book_name, '^(%d+)(.*)', '%2%1');
				it = it .. '|data-sort-value="' .. sort_key .. '"|'  .. book_name .. "\n";
			else
				it = it .. "|\n";
			end
		end
		table.insert(result, {book.order, it});
	end
	-- sort the results before we dump it out
	table.sort(result, function( a, b ) return a[1] < b[1] end);
	local it = '{| class="wikitable sortable"\n';
	it = it .. '!data-sort-type="number"|順序 !!代碼 !! 基督教名 !! 天主教名 !! 東正教名 !! 英文名\n';
	for i = 1, #result do
		it = it .. mw.ustring.gsub(result[i][2], '&TK;', i);
	end
	it = it .. "|}";
	return it;
end

p.dump_versions = function( frame )
	local it;
	local order = {};
	local conjunction = frame.args['conjunction'] or '同';
	-- pass 1
	for id, spec in pairs(versions) do
		table.insert(order, id);
	end
	if #order > 0 then
		table.sort(order);
		-- pass 2
		it = '';
		for i = 1, #order do
			local id = order[i];
			local label = id;
			local stag = '';
			local etag = '';
			if versions[id].link then
				stag = '[[' .. versions[id].link .. '|';
				etag = ']]';
			end
			if versions[id].names then
				label = label .. '(' .. stag .. versions[id].names[1] .. etag .. ')';
			else
				label = stag .. label .. etag;
			end
			if i == #order then
				it = it .. ' ' .. conjunction .. ' ';
			elseif i > 1 then
				it = it .. '、';
			end
			it = it .. label;
		end
	end
	return it;
end

-- Book listing function for actual articles
p.list_books = function( frame )
	local it;
	local id = frame.args[1] or frame.args.version;
	if not id then
		croak('冇畀參數,唔知要列乜出來');
	elseif not versions[id] then
		croak('唔知 「' .. id .. '」 係乜嘢譯本嘅代碼');
	else
		local version = versions[id];
		local parts = version.parts;
		local abbreviations_exist_p = false;
		for pass = 1, 2 do
			if pass == 2 then
				it = '{| class="wikitable sortable"\n';
				it = it .. '!data-sort-type="number"|順序\n';
				it = it .. "!" .. languages[version.tradition] .. "卷名\n";
				if abbreviations_exist_p then
					it = it .. "!縮寫\n";
				end
				if version.number_of_chapters then
					it = it .. '!data-sort-type="number"|章數\n';
				end
				if languages[version.tradition] ~= '英文' then
					it = it .. "!英文卷名對照\n";
				end
			end
			local n = 1;
			for i = 1, #parts do
				for j = 1, #(parts[i].books) do
					local id = parts[i].books[j];
					local abbreviation = find_abbreviation(id, version.tradition, nil, true, false);
					if pass == 1 then
						if abbreviation then
							abbreviations_exist_p = true;
						end
					else
						it = it .. "|-\n";
						it = it .. '| ' .. n .. "\n";
						local book_name = find_book_name(id, version.tradition, nil);
						local link_target = find_book_name(id, version.tradition, 'full');
						if books[id].protestant then
							link_target = find_book_name(id, 'protestant');
						end
						it = it .. '| [[' .. link_target .. '|' .. book_name .. "]]\n";
						if abbreviations_exist_p then
							it = it .. '| ' .. (abbreviation or '') .. "\n";
						end
						if version.number_of_chapters then
							local m = version.number_of_chapters[id];
							if m then
								it = it .. '| ' .. m .. "\n";
							else
								it = it .. "|\n";
							end
						end
						if languages[version.tradition] ~= '英文' then
							local english_name = find_book_name(id, 'english');
							local english_sort_key = mw.ustring.gsub(english_name, '^(%d+)(.*)', '%2%1');
							it = it .. '|data-sort-value="' ..  english_sort_key .. '"|' .. english_name .. "\n";
						end
						n = n + 1;
					end
				end
			end
			if pass == 2 then
				it = it .. "|}";
			end
		end
	end
	return it;
end

--- Test cases ----------------------------------------------------------------
--
-- Unfortunately this module was not written the correct way (SE-wise);
-- viz. tests were not written first, so a lot of test cases are missing

p.test = function ()
	-- test case for crash observed 2023-02-24
	local Gn_6_4 = {
		['book'] = 'Gn';
		['chapter'] = '6';
		['verses'] = '4';
	};
	local det = format_numbering(Gn_6_4, 'judaism', 'JPS');
	assert(mw.ustring.match(det, 'JPS'));	-- Make sure it does not crash
	assert(mw.ustring.match(det, 'G'));		-- JPS is an English translation
	assert(not mw.ustring.match(det, '〈'));
	assert(mw.ustring.match(det, ' '));
	assert(mw.ustring.match(det, 'pt0106%.htm'));
	-- test case for unrecognized book observed 2023-04-09
	assert(version_has_book_p('JPS', '1Kg'));
	assert(version_has_book_p('JPS', '2Kg'));
	assert(version_has_book_p('JPS', '1Ch'));
	assert(version_has_book_p('JPS', '2Ch'));
	assert(version_has_book_p('JPS', 'Ezr'));
	assert(version_has_book_p('JPS', 'Neh'));
	assert_canonical_book_code('1Kg');
	local I_Kg_17 = {
		['book'] = '1Kg';
		['chapter'] = '17';
		['version'] = 'JPS';
	};
	assert(construct_formatted_reference(I_Kg_17));
end

--- Non-invocable internal functions exported for other modules to use --------

return p;