Compare commits

..

4 Commits

Author SHA1 Message Date
Richard Wong f169e18ea3
Feat: added up to dep6-3 2024-08-19 17:35:31 +09:00
Richard Wong 31772fec80
Bug: fixed korean empty string issue
Feat: combined Answer and Diff buttons into a single diff button
2024-08-19 16:21:54 +09:00
Richard Wong 32363c9d7b
Feat: implement i18n support 2024-07-01 19:21:15 +09:00
Richard Wong 2e29c70d17
Fix: remove bug causing clicking on text linking to first box 2024-07-01 16:26:11 +09:00
11 changed files with 2165 additions and 1639 deletions

View File

@ -11,9 +11,13 @@
}, },
"dependencies": { "dependencies": {
"@fontsource/montserrat": "^4.5.14", "@fontsource/montserrat": "^4.5.14",
"i18next": "^23.11.5",
"i18next-browser-languagedetector": "^8.0.0",
"i18next-http-backend": "^2.5.2",
"react": "^18.2.0", "react": "^18.2.0",
"react-checkbox-tree": "^1.8.0", "react-checkbox-tree": "^1.8.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-i18next": "^14.1.2",
"react-string-diff": "^0.2.0", "react-string-diff": "^0.2.0",
"underscore": "^1.13.6" "underscore": "^1.13.6"
}, },
@ -26,5 +30,6 @@
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.3.4", "eslint-plugin-react-refresh": "^0.3.4",
"vite": "^4.3.2" "vite": "^4.3.2"
} },
"packageManager": "pnpm@8.15.4+sha512.0bd3a9be9eb0e9a692676deec00a303ba218ba279d99241475616b398dbaeedd11146f92c2843458f557b1d127e09d4c171e105bdcd6b61002b39685a8016b9e"
} }

View File

@ -8,6 +8,15 @@ dependencies:
'@fontsource/montserrat': '@fontsource/montserrat':
specifier: ^4.5.14 specifier: ^4.5.14
version: 4.5.14 version: 4.5.14
i18next:
specifier: ^23.11.5
version: 23.11.5
i18next-browser-languagedetector:
specifier: ^8.0.0
version: 8.0.0
i18next-http-backend:
specifier: ^2.5.2
version: 2.5.2
react: react:
specifier: ^18.2.0 specifier: ^18.2.0
version: 18.2.0 version: 18.2.0
@ -17,6 +26,9 @@ dependencies:
react-dom: react-dom:
specifier: ^18.2.0 specifier: ^18.2.0
version: 18.2.0(react@18.2.0) version: 18.2.0(react@18.2.0)
react-i18next:
specifier: ^14.1.2
version: 14.1.2(i18next@23.11.5)(react-dom@18.2.0)(react@18.2.0)
react-string-diff: react-string-diff:
specifier: ^0.2.0 specifier: ^0.2.0
version: 0.2.0(react@18.2.0) version: 0.2.0(react@18.2.0)
@ -244,6 +256,13 @@ packages:
'@babel/helper-plugin-utils': 7.21.5 '@babel/helper-plugin-utils': 7.21.5
dev: true dev: true
/@babel/runtime@7.24.7:
resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==}
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: 0.14.1
dev: false
/@babel/template@7.20.7: /@babel/template@7.20.7:
resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
@ -808,6 +827,14 @@ packages:
resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
dev: true dev: true
/cross-fetch@4.0.0:
resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==}
dependencies:
node-fetch: 2.7.0
transitivePeerDependencies:
- encoding
dev: false
/cross-spawn@7.0.3: /cross-spawn@7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@ -1306,6 +1333,32 @@ packages:
function-bind: 1.1.1 function-bind: 1.1.1
dev: true dev: true
/html-parse-stringify@3.0.1:
resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
dependencies:
void-elements: 3.1.0
dev: false
/i18next-browser-languagedetector@8.0.0:
resolution: {integrity: sha512-zhXdJXTTCoG39QsrOCiOabnWj2jecouOqbchu3EfhtSHxIB5Uugnm9JaizenOy39h7ne3+fLikIjeW88+rgszw==}
dependencies:
'@babel/runtime': 7.24.7
dev: false
/i18next-http-backend@2.5.2:
resolution: {integrity: sha512-+K8HbDfrvc1/2X8jpb7RLhI9ZxBDpx3xogYkQwGKlWAUXLSEGXzgdt3EcUjLlBCdMwdQY+K+EUF6oh8oB6rwHw==}
dependencies:
cross-fetch: 4.0.0
transitivePeerDependencies:
- encoding
dev: false
/i18next@23.11.5:
resolution: {integrity: sha512-41pvpVbW9rhZPk5xjCX2TPJi2861LEig/YRhUkY+1FQ2IQPS0bKUDYnEqY8XPPbB48h1uIwLnP9iiEfuSl20CA==}
dependencies:
'@babel/runtime': 7.24.7
dev: false
/ignore@5.2.4: /ignore@5.2.4:
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
engines: {node: '>= 4'} engines: {node: '>= 4'}
@ -1558,6 +1611,18 @@ packages:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: true dev: true
/node-fetch@2.7.0:
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
engines: {node: 4.x || >=6.0.0}
peerDependencies:
encoding: ^0.1.0
peerDependenciesMeta:
encoding:
optional: true
dependencies:
whatwg-url: 5.0.0
dev: false
/node-releases@2.0.10: /node-releases@2.0.10:
resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==} resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==}
dev: true dev: true
@ -1733,6 +1798,26 @@ packages:
scheduler: 0.23.0 scheduler: 0.23.0
dev: false dev: false
/react-i18next@14.1.2(i18next@23.11.5)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-FSIcJy6oauJbGEXfhUgVeLzvWBhIBIS+/9c6Lj4niwKZyGaGb4V4vUbATXSlsHJDXXB+ociNxqFNiFuV1gmoqg==}
peerDependencies:
i18next: '>= 23.2.3'
react: '>= 16.8.0'
react-dom: '*'
react-native: '*'
peerDependenciesMeta:
react-dom:
optional: true
react-native:
optional: true
dependencies:
'@babel/runtime': 7.24.7
html-parse-stringify: 3.0.1
i18next: 23.11.5
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
/react-is@16.13.1: /react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
@ -1758,6 +1843,10 @@ packages:
loose-envify: 1.4.0 loose-envify: 1.4.0
dev: false dev: false
/regenerator-runtime@0.14.1:
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
dev: false
/regexp.prototype.flags@1.5.0: /regexp.prototype.flags@1.5.0:
resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@ -1929,6 +2018,10 @@ packages:
engines: {node: '>=4'} engines: {node: '>=4'}
dev: true dev: true
/tr46@0.0.3:
resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
dev: false
/type-check@0.4.0: /type-check@0.4.0:
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
@ -2011,6 +2104,22 @@ packages:
fsevents: 2.3.2 fsevents: 2.3.2
dev: true dev: true
/void-elements@3.1.0:
resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
engines: {node: '>=0.10.0'}
dev: false
/webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
dev: false
/whatwg-url@5.0.0:
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
dependencies:
tr46: 0.0.3
webidl-conversions: 3.0.1
dev: false
/which-boxed-primitive@1.0.2: /which-boxed-primitive@1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
dependencies: dependencies:

View File

@ -0,0 +1,93 @@
{
"main": {
"title": "Scripture Memory Tester",
"pick_lang": "Pick Language",
"pick_num_verses": "Pick Number of Verses:",
"num_verses_tested": "Number of Verses Tested:",
"note_num_verses": "(It will only give you as many verses as there are in selected packs)",
"set_shuffle": "Set Shuffle:",
"note_set_shuffle": "(Otherwise cards will appear in sequential order)",
"hide_reference": "Set Hide Reference:",
"note_hide_reference": "(If you also want to test the verse reference)",
"pick_pack": "Pick Your Packs:",
"shuffle_card": "Shuffle Cards:",
"verses": "Verses:"
},
"verse_validator": {
"input_reference": "Input Verse Reference:",
"input_chapter_title": "Input Chapter Title:",
"input_title": "Input Title:",
"input_verse": "Input Verse:"
},
"nodes": [
{
"value": "loa",
"label": "Lessons on Assurance"
},
{
"value": "tms60",
"label": "TMS60",
"children": [
{ "value": "tms-60-pack-a", "label": "Living the New Life" },
{ "value": "tms-60-pack-b", "label": "Proclaiming Christ" },
{ "value": "tms-60-pack-c", "label": "Reliance on God's Resources" },
{ "value": "tms-60-pack-d", "label": "Being Christ's Disciple" },
{ "value": "tms-60-pack-e", "label": "Growth in Christlikeness" }
]
},
{
"value": "dep-1",
"label": "DEP 1"
},
{
"value": "dep-2",
"label": "DEP 2",
"children": [
{ "value": "dep-2-part-a", "label": "Why do we have Quiet Time?" },
{ "value": "dep-2-part-b", "label": "What is Quiet Time?" },
{ "value": "dep-2-part-c", "label": "Examples of Quiet Time" }
]
},
{
"value": "dep-3",
"label": "DEP 3",
"children": [
{ "value": "dep-3-part-a", "label": "Authority of the Word" },
{ "value": "dep-3-part-b", "label": "value of the Word" },
{ "value": "dep-3-part-c", "label": "Attitude to the Word" },
{ "value": "dep-3-part-d", "label": "How to take in the Word (Word Hand Illustration)" }
]
},
{
"value": "dep-4",
"label": "DEP 4",
"children": [
{ "value": "dep-4-part-a", "label": "Command of Prayer" },
{ "value": "dep-4-part-b", "label": "Promises and Blessings of Prayer" },
{ "value": "dep-4-part-c", "label": "Conditions for Answered Prayer" },
{ "value": "dep-4-part-d", "label": "Examples of Prayer" },
{ "value": "dep-4-part-e", "label": "Prayer Hand Illustration" }
]
},
{
"value": "dep-5",
"label": "DEP 5",
"children": [
{ "value": "dep-5-part-a", "label": "Foundation of Christian Fellowship" },
{ "value": "dep-5-part-b", "label": "Importance of fellowship" },
{ "value": "dep-5-part-c", "label": "Essentials of fellowship" },
{ "value": "dep-5-part-d", "label": "Attitude of fellowship" },
{ "value": "dep-5-part-e", "label": "Problem solving in fellowship" }
]
},
{
"value":"dep-6",
"label":"DEP 6",
"children": [
{ "value": "dep-6-part-a", "label": "Who is respondible for witnessing?" },
{ "value": "dep-6-part-b", "label": "Why should we witness?" },
{ "value": "dep-6-part-c", "label": "How do we witness?" }
]
}
]
}

View File

@ -0,0 +1,28 @@
{
"main": {
"title": "성경 암송 앱",
"pick_lang": "언어 선택",
"pick_num_verses": "암송 구절 선택:",
"num_verses_tested": "구절 수 선택:",
"note_num_verses": "(It will only give you as many verses as there are in selected packs)",
"set_shuffle": "무작위 설정:",
"note_set_shuffle": "(Otherwise cards will appear in sequential order)",
"hide_reference": "Set Hide Reference:",
"note_hide_reference": "(If you also want to test the verse reference)",
"pick_pack": "Pick Your Packs:",
"shuffle_card": "Shuffle Cards:",
"verses": "Verses:"
},
"verse_validator": {
"input_reference": "Input Verse Reference: (KR)",
"input_chapter_title": "Input Chapter Title: (KR)",
"input_title": "Input Title: (KR)",
"input_verse": "Input Verse: (KR)"
},
"nodes": [
{
"value": "loa",
"label": "Lessons on Assurance"
}
]
}

View File

@ -3,15 +3,18 @@ Implemented features:
- read keys from json - read keys from json
- create checklist from keys - create checklist from keys
*/ */
import VerseData from "./assets/verse.json" import fullVerseData from "./assets/verse.json" // the actual verse json data file
import { useState } from "react"; import { useState } from "react";
import CheckboxTree from 'react-checkbox-tree'; import CheckboxTree from 'react-checkbox-tree';
import 'react-checkbox-tree/lib/react-checkbox-tree.css'; import 'react-checkbox-tree/lib/react-checkbox-tree.css';
import _ from 'underscore'; import _ from 'underscore';
import './VerseSampler.css' import './VerseSampler.css'
import VerseValidator from "./VerseValidator"; import VerseValidator from "./VerseValidator";
import { useTranslation } from 'react-i18next';
import logo from './assets/droplet.svg';
import { Suspense } from "react";
const GenerateTestList = ({ packs, testCount, toShuffle, toHideReference}) => { const GenerateTestList = ({ VerseData, packs, testCount, toShuffle, toHideReference, translate}) => {
let testList = packs.reduce( let testList = packs.reduce(
// grab all elements included checked in "packs" // grab all elements included checked in "packs"
(accumulator, currentValue) => accumulator.concat(VerseData[currentValue]), (accumulator, currentValue) => accumulator.concat(VerseData[currentValue]),
@ -19,84 +22,28 @@ const GenerateTestList = ({ packs, testCount, toShuffle, toHideReference}) => {
); );
testList = toShuffle ? _.sample(testList, testCount) : _.first(testList, testCount); testList = toShuffle ? _.sample(testList, testCount) : _.first(testList, testCount);
return ( return (
<ArrayTester array={testList} toHideReference={toHideReference} /> <ArrayTester
array={testList}
toHideReference={toHideReference}
translate={translate}
/>
) )
} }
const ArrayTester = ({ array, toHideReference }) => { const ArrayTester = ({ array, toHideReference, translate}) => {
const list = array.map((element) => ( const list = array.map((element) => (
// key needs to be unique; chose 3 elements that will separate all elements // key needs to be unique; chose 3 elements that will separate all elements
<VerseValidator key={element.pack + element.title + element.reference} <VerseValidator
element={element} toHideReference={toHideReference} /> key={element.pack + element.title + element.reference}
element={element}
toHideReference={toHideReference}
t={translate} // this passes the t i18 object to the function
/>
)) ))
return list return list
} }
const CheckboxWidget = ({nodes, checked, expanded, setChecked, setExpanded}) => {
const nodes = [
{
value: "loa",
label: "Lessons on Assurance",
},
{
value: "tms60",
label: "TMS60",
children: [
{ value: "tms-60-pack-a", label: "Living the New Life" },
{ value: "tms-60-pack-b", label: "Proclaiming Christ" },
{ value: "tms-60-pack-c", label: "Reliance on God's Resources" },
{ value: "tms-60-pack-d", label: "Being Christ's Disciple" },
{ value: "tms-60-pack-e", label: "Growth in Christlikeness" }
],
},
{
value: "dep-1",
label: "DEP 1",
},
{
value: "dep-2",
label: "DEP 2",
children: [
{ value: "dep-2-part-a", label: "Why do we have Quiet Time?" },
{ value: "dep-2-part-b", label: "What is Quiet Time?" },
{ value: "dep-2-part-c", label: "Examples of Quiet Time" }
],
},
{
value: "dep-3",
label: "DEP 3",
children: [
{ value: "dep-3-part-a", label: "Authority of the Word" },
{ value: "dep-3-part-b", label: "Value of the Word" },
{ value: "dep-3-part-c", label: "Attitude to the Word" },
{ value: "dep-3-part-d", label: "How to take in the Word (Word Hand Illustration)" }
],
},
{
value: "dep-4",
label: "DEP 4",
children: [
{ value: "dep-4-part-a", label: "Command of Prayer" },
{ value: "dep-4-part-b", label: "Promises and Blessings of Prayer" },
{ value: "dep-4-part-c", label: "Conditions for Answered Prayer" },
{ value: "dep-4-part-d", label: "Examples of Prayer" },
{ value: "dep-4-part-e", label: "Prayer Hand Illustration" }
],
},
{
value: "dep-5",
label: "DEP 5",
children: [
{ value: "dep-5-part-a", label: "Foundation of Christian Fellowship" },
{ value: "dep-5-part-b", label: "Importance of fellowship" },
{ value: "dep-5-part-c", label: "Essentials of fellowship" },
{ value: "dep-5-part-d", label: "Attitude of fellowship" },
{ value: "dep-5-part-e", label: "Problem solving in fellowship" }
],
},
];
const CheckboxWidget = ({checked, expanded, setChecked, setExpanded}) => {
return ( return (
<div className="CheckboxTree"> <div className="CheckboxTree">
<CheckboxTree <CheckboxTree
@ -110,10 +57,41 @@ const CheckboxWidget = ({checked, expanded, setChecked, setExpanded}) => {
); );
} }
// loadCustomData
const loadCustomData = (language) => {
let data;
// console.log(language)
switch (language) {
case 'kn':
data = fullVerseData.kn;
break;
case 'en':
default:
data = fullVerseData.en;
break;
}
return data;
};
function Page() {
// setup i18 for function
const { t, i18n } = useTranslation();
// load VerseData json data file
const [VerseData, setVerseData] = useState(loadCustomData(i18n.language));
// function hook to change language
// updates both i18n language and also the VerseData state variable
const changeLanguage = (lng) => {
i18n.changeLanguage(lng);
setVerseData(loadCustomData(i18n.language));
};
function App() {
// create checklist array for pack selection // create checklist array for pack selection
const packList = Object.keys(VerseData); const packList = Object.keys(VerseData);
// return a list of packObj's // return a list of packObj's
@ -178,10 +156,13 @@ function App() {
return ( return (
<div className="App"> <div className="App">
<h1>Scripture Memory Tester</h1> <h1>{t('main.title')}</h1>
<h2>Pick Number of Verses:</h2> <h2>{t('main.pick_lang')}</h2>
<button type="button" onClick={() => changeLanguage('en')}>English</button>
<button type="button" onClick={() => changeLanguage('kn')}>Korean</button>
<h2>{t('main.pick_num_verses')}</h2>
<label className="test-count-box-label" htmlFor="testCountBox"> <label className="test-count-box-label" htmlFor="testCountBox">
Number of Verses Tested: {t('main.num_verses_tested')}
</label> </label>
<input <input
className="test-count-box" className="test-count-box"
@ -192,37 +173,38 @@ function App() {
onChange={testCountChange} onChange={testCountChange}
/> />
<p>(It will only give you as many verses as there are in selected packs)</p> <p>{t('main.note_num_verses')}</p>
<h2> <h2>
Set Shuffle: {t('main.set_shuffle')}
<input <input
type="checkbox" type="checkbox"
checked={toShuffle} checked={toShuffle}
onChange={handleShuffleCheckboxChange} onChange={handleShuffleCheckboxChange}
/> />
</h2> </h2>
<p>(Otherwise cards will appear in sequential order)</p> <p>{t('main.note_set_shuffle')}</p>
<div> <div>
{!toShuffle ? {!toShuffle ?
<> <>
<h2> <h2>
Set Hide Reference: {t('main.hide_reference')}
<input <input
type="checkbox" type="checkbox"
checked={toHideReference} checked={toHideReference}
onChange={handleHideReferenceCheckboxChange} onChange={handleHideReferenceCheckboxChange}
/> />
</h2> </h2>
<p>(If you also want to test the verse reference)</p> <p>{t('main.note_hide_reference')}</p>
</>: </>:
<p></p>} <p></p>}
</div> </div>
<h2>Pick Your Packs:</h2> <h2>{t('main.pick_pack')}</h2>
<CheckboxWidget <CheckboxWidget
nodes={t('nodes', { returnObjects: true })}
checked={checked} checked={checked}
expanded={expanded} expanded={expanded}
setChecked={setChecked} setChecked={setChecked}
@ -232,18 +214,20 @@ function App() {
<div key={refreshKey}> <div key={refreshKey}>
{toShuffle ? {toShuffle ?
<> <>
<h2>Shuffle Cards:</h2> <h2>{t('main.shuffle_card')}</h2>
<RefreshButton onClick={handleRefresh} /> <RefreshButton onClick={handleRefresh} />
</>: </>:
<p></p>} <p></p>}
</div> </div>
<h1>Verses:</h1> <h1>{t('main.verses')}</h1>
<GenerateTestList <GenerateTestList
VerseData={VerseData}
packs={checked} packs={checked}
testCount={testCount} testCount={testCount}
toShuffle={toShuffle} toShuffle={toShuffle}
toHideReference={toHideReference} toHideReference={toHideReference}
translate={t}
/> />
<hr /> <hr />
@ -253,4 +237,18 @@ function App() {
); );
} }
export default App // loading component for suspense fallback
const Loader = () => (
<div className="App">
<img src={logo} className="App-logo" alt="logo" />
<div>loading...</div>
</div>
);
export default function App() {
return (
<Suspense fallback={<Loader />}>
<Page />
</Suspense>
);
}

View File

@ -39,6 +39,11 @@
font-size: 15px; font-size: 15px;
} }
.reference-label .main-title-box-label .title-box-label .verse-box-label {
pointer-events: none;
}
.verse-box { .verse-box {
max-width:400px; max-width:400px;
height: 10vh; height: 10vh;

View File

@ -4,7 +4,7 @@ import { StringDiff } from "react-string-diff";
// function to render and handle logic of each of the cells // function to render and handle logic of each of the cells
const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse } , toHideReference}) => { // useful use of destructuring here const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse } , toHideReference, t}) => { // useful use of destructuring here
const [inputReference, setReference] = useState('') const [inputReference, setReference] = useState('')
const [referenceBool, setReferenceBool] = useState(false) const [referenceBool, setReferenceBool] = useState(false)
@ -21,16 +21,15 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
// function to check correctness of verse input // function to check correctness of verse input
// so far only perform checking on full spelling of reference names // so far only perform checking on full spelling of reference names
const referenceChange = (e) => { const referenceChange = (e) => {
const value = e.target.value; const value = e.target.value;
const string1 = String(value) const string1 = String(value)
.replace(/\s+/g, "") .replace(/\s+/g, "")
.toLowerCase(); .toLowerCase()
.normalize("NFC");
const string2 = String(reference) const string2 = String(reference)
.replace(/\s+/g, "") .replace(/\s+/g, "")
.toLowerCase(); .toLowerCase()
.normalize("NFC");
const bool = (string1 === string2); const bool = (string1 === string2);
setReference(value); setReference(value);
@ -43,13 +42,15 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
let string1 = value; let string1 = value;
let string2 = verse; let string2 = verse;
string1 = String(string1) string1 = String(string1)
.replace(/[^\w\s]/g, "") .replace(/[\p{P}\p{S}]/gu, "")
.replace(/\s+/g, "") .replace(/\s+/g, "")
.toLowerCase(); .toLowerCase()
.normalize("NFC");
string2 = String(string2) string2 = String(string2)
.replace(/[^\w\s]/g, "") .replace(/[\p{P}\p{S}]/gu, "")
.replace(/\s+/g, "") .replace(/\s+/g, "")
.toLowerCase(); .toLowerCase()
.normalize("NFC");
const bool = string1 === string2; const bool = string1 === string2;
@ -63,13 +64,16 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
let string1 = value; let string1 = value;
let string2 = title; let string2 = title;
string1 = String(string1) string1 = String(string1)
.replace(/[^\w\s]/g, "") .replace(/[\p{P}\p{S}]/gu, "") // Removes punctuation and symbols
.replace(/\s+/g, "") .replace(/\s+/g, "") // Removes all whitespace
.toLowerCase(); .toLowerCase()
.normalize("NFC"); // Normalizes to NFC form
string2 = String(string2) string2 = String(string2)
.replace(/[^\w\s]/g, "") .replace(/[\p{P}\p{S}]/gu, "")
.replace(/\s+/g, "") .replace(/\s+/g, "")
.toLowerCase(); .toLowerCase()
.normalize("NFC");
const bool = string1 === string2; const bool = string1 === string2;
@ -80,17 +84,22 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
{/* function to check correctness of chapter title input */} {/* function to check correctness of chapter title input */}
const chapterTitleChange = (e) => { const chapterTitleChange = (e) => {
const value = e.target.value; const value = e.target.value;
let string1 = value; let string1 = value;
let string2 = chapterTitle; let string2 = chapterTitle;
string1 = String(string1) string1 = String(string1)
.replace(/[^\w\s]/g, "") .replace(/[\p{P}\p{S}]/gu, "")
.replace(/\s+/g, "") .replace(/\s+/g, "")
.toLowerCase(); .toLowerCase()
.normalize("NFC");
string2 = String(string2) string2 = String(string2)
.replace(/[^\w\s]/g, "") .replace(/[\p{P}\p{S}]/gu, "")
.replace(/\s+/g, "") .replace(/\s+/g, "")
.toLowerCase(); .toLowerCase()
.normalize("NFC");
const bool = string1 === string2; const bool = string1 === string2;
@ -100,12 +109,27 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
const DiffViewer = ({oldValue, newValue}) => { const DiffViewer = ({oldValue, newValue}) => {
const string1 = String(oldValue) const string1 = String(oldValue)
.replace(/[^\w\s]/g, "") .replace(/[\p{P}\p{S}]/gu, "")
.toLowerCase(); .toLowerCase()
.normalize("NFC");
const string2 = String(newValue) const string2 = String(newValue)
.replace(/[^\w\s]/g, "") .replace(/[\p{P}\p{S}]/gu, "")
.toLowerCase(); .toLowerCase()
.normalize("NFC");
return (<StringDiff oldValue={string1} newValue={string2} diffMethod="diffWords" />)
}
const DiffViewerStrict = ({oldValue, newValue}) => {
const string1 = String(oldValue)
.toLowerCase()
.normalize("NFC");
const string2 = String(newValue)
.toLowerCase()
.normalize("NFC");
return (<StringDiff oldValue={string1} newValue={string2} diffMethod="diffWords" />) return (<StringDiff oldValue={string1} newValue={string2} diffMethod="diffWords" />)
} }
@ -116,8 +140,8 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
{/* toggle hiding reference */} {/* toggle hiding reference */}
{toHideReference ? ( {toHideReference ? (
<div> <div>
<label className="reference-label" htmlFor="referenceBox"> <label className="reference-label">
Input Verse Reference: {t('verse_validator.input_reference')}
</label> </label>
<textarea <textarea
className={`reference-box${referenceBool ? " correct" : ""}`} className={`reference-box${referenceBool ? " correct" : ""}`}
@ -136,8 +160,8 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
{/* toggle chapterTitle */} {/* toggle chapterTitle */}
{chapterTitle && ( {chapterTitle && (
<div> <div>
<label className="main-title-box-label" htmlFor="chapterTitleBox"> <label className="main-title-box-label">
Input Chapter Title: {t('verse_validator.input_chapter_title')}
</label> </label>
<textarea <textarea
className={`chapter-title-box${chapterTitleBool ? " correct" : ""}`} className={`chapter-title-box${chapterTitleBool ? " correct" : ""}`}
@ -150,8 +174,8 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
)} )}
{/* input box for title */} {/* input box for title */}
<label className="title-box-label" htmlFor="titleBox"> <label className="title-box-label">
Input Title: {t('verse_validator.input_title')}
</label> </label>
<textarea <textarea
className={`title-box${titleBool ? " correct" : ""}`} className={`title-box${titleBool ? " correct" : ""}`}
@ -162,8 +186,8 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
/> />
{/* input box for verse */} {/* input box for verse */}
<label className="verse-box-label" htmlFor="verseBox"> <label className="verse-box-label">
Input Verse: {t('verse_validator.input_verse')}
</label> </label>
<textarea <textarea
className={`verse-box${verseBool ? " correct" : ""}`} className={`verse-box${verseBool ? " correct" : ""}`}
@ -175,29 +199,25 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
{/* button to toggle show answer*/} {/* button to toggle show answer*/}
<div className="answer-button-box"> <div className="answer-button-box">
<button onClick={() => setHintBool(!hintBool)}>Show Answer:</button> {/* <button onClick={() => setHintBool(!hintBool)}>Show Answer:</button> */}
<button onClick={() => setDiffBool(!diffBool)}>Show Diff:</button> <button onClick={() => setDiffBool(!diffBool)}>Show Answer:</button>
</div> </div>
{/* This shows the answers*/}
{hintBool && (
<div className="answer-box">
<h3>Answers</h3>
<p>Reference:<br />{reference}</p>
{chapterTitle && (
<>
<p>ChapterTitle<br />{chapterTitle}</p>
</>
)}
<p>Title:<br />{title}</p>
<p>Verse:<br />{verse}</p>
</div>
)}
{/* This shows the difference between given and input answers*/} {/* This shows the difference between given and input answers*/}
{diffBool && ( {diffBool && (
<div className="diff-box"> <div className="diff-box">
<h3>Differences</h3> <h3>Differences</h3>
<p></p>
<div>
Reference:
<DiffViewerStrict
oldValue={reference}
newValue={inputReference}
/>
</div>
<p></p>
{chapterTitle && ( {chapterTitle && (
<div> <div>
ChapterTitle: ChapterTitle:
@ -207,13 +227,23 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
/> />
</div> </div>
)} )}
<p></p> <p></p>
<div> <div>
Title: <DiffViewer oldValue={title} newValue={inputTitle} /> Title:
<DiffViewer
oldValue={title}
newValue={inputTitle}
/>
</div> </div>
<p></p> <p></p>
<div> <div>
Verse: <DiffViewer oldValue={verse} newValue={inputVerse} /> Verse:
<DiffViewer
oldValue={verse}
newValue={inputVerse}
/>
</div> </div>
</div> </div>
)} )}

4
src/assets/droplet.svg Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19 13.8C19 17.7764 15.866 21 12 21C8.13401 21 5 17.7764 5 13.8C5 12.8452 5.18069 11.9338 5.50883 11.1C6.54726 8.46135 12 3 12 3C12 3 17.4527 8.46135 18.4912 11.1C18.8193 11.9338 19 12.8452 19 13.8Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 522 B

View File

@ -1,3 +1,5 @@
{
"en":
{ {
"loa": [ "loa": [
{ {
@ -648,7 +650,7 @@
"pack": "DEP 2", "pack": "DEP 2",
"title": "To discern and obey God's will", "title": "To discern and obey God's will",
"chapterTitle": "Why do we have Quiet Time", "chapterTitle": "Why do we have Quiet Time",
"reference": "Psalm 143:8,10", "reference": "Psalms 143:8,10",
"verse": "Let the morning bring me word of your unfailing love, for I have put my trust in you. Show me the way I should go, for to you I lift up my soul. Teach me to do your will, for you are my God; may your good Spirit lead me on level ground." "verse": "Let the morning bring me word of your unfailing love, for I have put my trust in you. Show me the way I should go, for to you I lift up my soul. Teach me to do your will, for you are my God; may your good Spirit lead me on level ground."
} }
], ],
@ -922,7 +924,7 @@
"pack": "DEP 3", "pack": "DEP 3",
"title": "Being our joy and delight", "title": "Being our joy and delight",
"chapterTitle": "Value of the Word", "chapterTitle": "Value of the Word",
"reference": "Psalm 119:111", "reference": "Psalms 119:111",
"verse": "Your statutes are my heritage forever; they are the joy of my heart." "verse": "Your statutes are my heritage forever; they are the joy of my heart."
}, },
{ {
@ -1503,5 +1505,227 @@
"reference": "Proverbs 28:13", "reference": "Proverbs 28:13",
"verse": "He who conceals his sins does not prosper, but whoever confesses and renounces them finds mercy." "verse": "He who conceals his sins does not prosper, but whoever confesses and renounces them finds mercy."
} }
],
"dep-6-part-a": [
{
"pack": "DEP 6",
"chapterTitle": "Who is reponsible for witnessing?",
"title": "Every Christian",
"reference": "Acts 1:8",
"verse": "But you will receive power when the Holy Spirit comes on you; and you will be my witnesses in Jerusalem, and in all Judea and Samaria, and to the ends of the earth."
},
{
"pack": "DEP 6",
"chapterTitle": "Who is reponsible for witnessing?",
"title": "Every Christian",
"reference": "2 Corinthians 5:18-19",
"verse": "All this is from God, who reconciled us to himself through Christ and gave us the ministry of reconciliation: that God was reconciling the world to himself in Christ, not counting men's sins against them. And he has committed to us the message of reconciliation."
}
],
"dep-6-part-b": [
{
"pack": "DEP 6",
"chapterTitle": "Why should we witness?",
"title": "God's command",
"reference": "2 Timothy 4:1-2",
"verse": "In the presence of God and of Christ Jesus, who will judge the living and the dead, and in view of his appearing and his kingdom, I give you this charge: Preach the Word; be prepared in season and out of season; correct, rebuke and encourage--with great patience and careful instruction."
},
{
"pack": "DEP 6",
"chapterTitle": "Why should we witness?",
"title": "Love for the lost",
"reference": "Luke 19:10",
"verse": "For the Son of Man came to seek and to save what was lost."
},
{
"pack": "DEP 6",
"chapterTitle": "Why should we witness?",
"title": "God's commission",
"reference": "1 Thessalonians 2:4",
"verse": "On the contrary, we speak as men approved by God to be entrusted with the gospel. We are not trying to please men but God, who tests our hearts."
},
{
"pack": "DEP 6",
"chapterTitle": "Why should we witness?",
"title": "Compelling role",
"reference": "1 Corinthians 9:16",
"verse": "Yet when I preach the gospel, I cannot boast, for I am compelled to preach. Woe to me if I do not preach the gospel!"
},
{
"pack": "DEP 6",
"chapterTitle": "Why should we witness?",
"title": "No preaching, no believing",
"reference": "Romans 10:14",
"verse": "How, then, can they call on the one they have not believed in? And how can they believe in the one of whom they have not heard? And how can they hear without someone preaching to them?"
},
{
"pack": "DEP 6",
"chapterTitle": "Why should we witness?",
"title": "God wants all men to be saved",
"reference": "1 Timothy 2:3-4",
"verse": "This is good, and pleases God our Savior, who wants all men to be saved and to come to a knowledge of the truth."
},
{
"pack": "DEP 6",
"chapterTitle": "Why should we witness?",
"title": "Gods great concern for men",
"reference": "Jonah 4:10-11",
"verse": "But the LORD said, You have been concerned about this vine, though you did not tend it or make it grow. It sprang up overnight and died overnight. But Nineveh has more than a hundred and twenty thousand people who cannot tell their right hand from their left, and many cattle as well. Should I not be concerned about that great city?"
},
{
"pack": "DEP 6",
"chapterTitle": "Why should we witness?",
"title": "God rejoices over one sinners repentance",
"reference": "Luke 15:7",
"verse": "I tell you that in the same way there will be more rejoicing in heaven over one sinner who repents than over ninety-nine righteous persons who do not need to repent."
},
{
"pack": "DEP 6",
"chapterTitle": "Why should we witness?",
"title": "To have blessed fellowship in the Lord",
"reference": "1 John 1:3",
"verse": "We proclaim to you what we have seen and heard, so that you also may have fellowship with us. And our fellowship is with the Father and with his Son, Jesus Christ."
},
{
"pack": "DEP 6",
"chapterTitle": "Why should we witness?",
"title": "Promise for everlasting glory",
"reference": "Daniel 12:3",
"verse": "Those who are wise will shine like the brightness of the heavens, and those who lead many to righteousness, like the stars for ever and ever."
}
],
"dep-6-part-c": [
{
"pack": "DEP 6",
"chapterTitle": "How do we witness?",
"title": "Follow Jesus first",
"reference": "Matthew 4:19",
"verse": "Come, follow me, Jesus said, and I will make you fishers of men."
},
{
"pack": "DEP 6",
"chapterTitle": "How do we witness?",
"title": "Live a life of good testimony",
"reference": "Philippians 2:15-16",
"verse": "so that you may become blameless and pure, children of God without fault in a crooked and depraved generation, in which you shine like stars in the universe as you hold out the word of life - in order that I may boast on the day of Christ that I did not run or labor for nothing."
},
{
"pack": "DEP 6",
"chapterTitle": "How do we witness?",
"title": "Pray for people",
"reference": "Ephesians 6:19",
"verse": "Pray also for me, that whenever I open my mouth, words may be given me so that I will fearlessly make known the mystery of the gospel,"
},
{
"pack": "DEP 6",
"chapterTitle": "How do we witness?",
"title": "Be prepared to answer",
"reference": "1 Peter 3:15",
"verse": "But in your hearts set apart Christ as Lord. Always be prepared to give an answer to everyone who asks you to give the reason for the hope that you have. But do this with gentleness and respect,"
},
{
"pack": "DEP 6",
"chapterTitle": "How do we witness?",
"title": "Share your testimony",
"reference": "John 4:39",
"verse": "Many of the Samaritans from that town believed in him because of the woman's testimony, He told me everything I ever did."
},
{
"pack": "DEP 6",
"chapterTitle": "How do we witness?",
"title": "Believe in the power of the Word",
"reference": "Hebrews 4:12",
"verse": "For the word of God is living and active. Sharper than any double-edged sword, it penetrates even to dividing soul and spirit, joints and marrow; it judges the thoughts and attitudes of the heart."
},
{
"pack": "DEP 6",
"chapterTitle": "How do we witness?",
"title": "The word preached never returns in vain",
"reference": "Isaiah 55:11",
"verse": "so is my word that goes out from my mouth: It will not return to me empty, but will accomplish what I desire and achieve the purpose for which I sent it."
},
{
"pack": "DEP 6",
"chapterTitle": "How do we witness?",
"title": "Focus on Jesus Christ when witnessing",
"reference": "1 Corinthians 1:23-24",
"verse": "but we preach Christ crucified: a stumbling block to Jews and foolishness to Gentiles, but to those whom God has called, both Jews and Greeks, Christ the power of God and the wisdom of God."
},
{
"pack": "DEP 6",
"chapterTitle": "How do we witness?",
"title": "Witness in power and conviction",
"reference": "1 Thessalonians 1:5",
"verse": "because our gospel came to you not simply with words, but also with power, with the Holy Spirit and with deep conviction. You know how we lived among you for your sake."
},
{
"pack": "DEP 6",
"chapterTitle": "How do we witness?",
"title": "Help them receive Jesus Christ",
"reference": "Romans 10:9-10",
"verse": "That if you confess with your mouth, Jesus is Lord, and believe in your heart that God raised him from the dead, you will be saved. For it is with your heart that you believe and are justified, and it is with your mouth that you confess and are saved."
},
{
"pack": "DEP 6",
"chapterTitle": "How do we witness?",
"title": "Encourage them to not delay the decision",
"reference": "2 Corinthians 6:2",
"verse": "For he says, In the time of my favor I heard you, and in the day of salvation I helped you. I tell you, now is the time of God's favor, now is the day of salvation."
},
{
"pack": "DEP 6",
"chapterTitle": "How do we witness?",
"title": "Help them be assured of their salvation",
"reference": "2 Corinthians 13:5",
"verse": "Examine yourselves to see whether you are in the faith; test yourselves. Do you not realize that Christ Jesus is in you--unless, of course, you fail the test?"
},
{
"pack": "DEP 6",
"chapterTitle": "How do we witness?",
"title": "Have an evangelical Bible study",
"reference": "Acts 17:2-3",
"verse": "As his custom was, Paul went into the synagogue, and on three Sabbath days he reasoned with them from the Scriptures, explaining and proving that the Christ had to suffer and rise from the dead. This Jesus I am proclaiming to you is the Christ, he said."
}
]
},
"kn":
{
"loa": [
{
"pack": "5확신",
"title": "구원의 확신",
"chapterTitle": "",
"reference": "요한일서 5:11-12",
"verse": "또 증거는 이것이니 하나님이 우리에게 영생을 주신 것과 이 생명이 그의 아들 안에 있는 그것이니라 아들이 있는 자에게는 생명이 있고 하나님의 아들이 없는 자에게는 생명이 없느니라"
},
{
"pack": "5확신",
"title": "기도 응답의 확신",
"chapterTitle": "",
"reference": "요한복음 16:24",
"verse": "지금까지는 너희가 내 이름으로 아무것도 구하지 아니하였으나, 구하라 그리하면 받으리니 너희 기쁨이 충만하리라"
},
{
"pack": "5확신",
"title": "승리의 확신",
"chapterTitle": "",
"reference": "고린도전서 10:13",
"verse": "사람이 감동할 시험밖에는 너희에게 당한 것이 없나니, 오직 하나님은 미쁘사 너희가 감당치 못할 시험 당함을 허락지 아니하시고, 시험당할 즈음에 또한 피할 길을 내사 너희로 능히 감당하게 히시느니라"
},
{
"pack": "5확신",
"title": "사죄의 확신",
"chapterTitle": "",
"reference": "요한일서 1:9",
"verse": "만일 우리가 우리 죄를 자백하면, 저는 미쁘시고 의로우사 우리 죄를 사하시며, 모든 불의에서 우리를 깨끗케 하실 것이요."
},
{
"pack": "5확신",
"title": "인도의 확신",
"chapterTitle": "",
"reference": "잠언 3:5-6",
"verse": "너는 마음을 다하여 여호와를 의뢰하고 네 명철을 의지하지 말라. 너는 범사에 그를 인정하라. 그리하면 네 길을 지도하시리라"
}
] ]
} }
}

27
src/i18n.js Normal file
View File

@ -0,0 +1,27 @@
import i18n from 'i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
i18n
// load translation using http -> see /public/locales
// learn more: https://github.com/i18next/i18next-http-backend
.use(Backend)
// detect user language
// learn more: https://github.com/i18next/i18next-browser-languageDetector
// note: I disabled detector to force en as default
.use(LanguageDetector)
// pass the i18n instance to react-i18next.
.use(initReactI18next) // default ./locales/en/translation.json
// init i18next
// for all options read: https://www.i18next.com/overview/configuration-options
.init({
fallbackLng: 'en',
detection: { // adds some caching
order: ['querystring', 'cookie', 'localStorage', 'navigator', 'htmlTag', 'path', 'subdomain'],
caches: ['localStorage', 'cookie'],
},
debug: true,
});
export default i18n;

View File

@ -4,6 +4,9 @@ import ReactDOM from 'react-dom/client'
import App from './VerseSampler.jsx' import App from './VerseSampler.jsx'
// import App from './MultiCheckbox.jsx' // import App from './MultiCheckbox.jsx'
// import Widget from './ReactCheckboxTree.jsx' // import Widget from './ReactCheckboxTree.jsx'
// import i18n (needs to be bundled ;))
import './i18n';
import './index.css' import './index.css'
import "@fontsource/montserrat" import "@fontsource/montserrat"