Fix: added hangul jamo decomposition to handle ios broken IME
This commit is contained in:
parent
faf83b4827
commit
4513592483
|
@ -1,6 +1,7 @@
|
|||
import { useState } from "react";
|
||||
import "./VerseValidator.css";
|
||||
import { StringDiff } from "react-string-diff";
|
||||
import { containsKorean, jamoSubstringMatch } from './jamoUtils';
|
||||
|
||||
const STATE = {
|
||||
INCORRECT: 0,
|
||||
|
@ -21,13 +22,38 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
|||
const [verseBool, setVerseBool] = useState(STATE.INCORRECT)
|
||||
const[hintBool, setHintBool] = useState(false)
|
||||
const[diffBool, setDiffBool] = useState(false)
|
||||
const [isComposing, setIsComposing] = useState(false);
|
||||
const [isComposing, setIsComposing] = useState(false);
|
||||
|
||||
|
||||
|
||||
// Handle the start of composition
|
||||
const handleCompositionStart = () => {
|
||||
setIsComposing(true);
|
||||
};
|
||||
|
||||
function resultChecker(string1, string2) {
|
||||
var result = STATE.INCORRECT; // init
|
||||
// contains korean
|
||||
if (containsKorean(string1)) {
|
||||
if (string1 === string2) {
|
||||
result = STATE.CORRECT;
|
||||
} else if (jamoSubstringMatch(string2, string1) & string1 !== "") {
|
||||
result = STATE.PARTIAL;
|
||||
} else {
|
||||
result = STATE.INCORRECT;
|
||||
}
|
||||
} else { // does not contain korean
|
||||
if (string1 === string2) {
|
||||
result = STATE.CORRECT;
|
||||
} else if (string2.startsWith(string1) & string1 !== "") {
|
||||
result = STATE.PARTIAL;
|
||||
} else {
|
||||
result = STATE.INCORRECT;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
// function to check correctness of reference input
|
||||
const referenceChange = (e) => {
|
||||
|
@ -40,14 +66,8 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
|||
.replace(/\s+/g, "")
|
||||
.toLowerCase()
|
||||
.normalize("NFC");
|
||||
var result = STATE.INCORRECT; // init
|
||||
if (string1 === string2) {
|
||||
result = STATE.CORRECT;
|
||||
} else if (string2.startsWith(string1) & string1 !== "") {
|
||||
result = STATE.PARTIAL
|
||||
} else {
|
||||
result = STATE.INCORRECT
|
||||
}
|
||||
|
||||
const result = resultChecker(string1, string2);
|
||||
|
||||
setReference(value);
|
||||
setReferenceBool(result);
|
||||
|
@ -79,14 +99,9 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
|||
.toLowerCase()
|
||||
.normalize("NFC");
|
||||
|
||||
var result = STATE.INCORRECT; // init
|
||||
if (string1 === string2) {
|
||||
result = STATE.CORRECT;
|
||||
} else if (string2.startsWith(string1) & string1 !== "") {
|
||||
result = STATE.PARTIAL
|
||||
} else {
|
||||
result = STATE.INCORRECT
|
||||
}
|
||||
|
||||
const result = resultChecker(string1, string2);
|
||||
|
||||
|
||||
setTitle(value);
|
||||
setTitleBool(result);
|
||||
|
@ -118,14 +133,7 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
|||
.toLowerCase()
|
||||
.normalize("NFC");
|
||||
|
||||
var result = STATE.INCORRECT; // init
|
||||
if (string1 === string2) {
|
||||
result = STATE.CORRECT;
|
||||
} else if (string2.startsWith(string1) & string1 !== "") {
|
||||
result = STATE.PARTIAL
|
||||
} else {
|
||||
result = STATE.INCORRECT
|
||||
}
|
||||
const result = resultChecker(string1, string2);
|
||||
|
||||
setChapterTitle(value);
|
||||
setChapterTitleBool(result);
|
||||
|
@ -153,14 +161,7 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
|||
.toLowerCase()
|
||||
.normalize("NFC");
|
||||
|
||||
var result = STATE.INCORRECT; // init
|
||||
if (string1 === string2) {
|
||||
result = STATE.CORRECT;
|
||||
} else if (string2.startsWith(string1) & string1 !== "") {
|
||||
result = STATE.PARTIAL
|
||||
} else {
|
||||
result = STATE.INCORRECT
|
||||
}
|
||||
const result = resultChecker(string1, string2);
|
||||
|
||||
setVerse(value);
|
||||
setVerseBool(result);
|
||||
|
@ -215,15 +216,11 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
|||
id="referenceBox"
|
||||
name="referenceBox"
|
||||
onChange={(event) => {
|
||||
|
||||
if (!isComposing) {
|
||||
referenceChange(event);
|
||||
}
|
||||
}}
|
||||
onCompositionStart={handleCompositionStart}
|
||||
onCompositionEnd={(event) => {
|
||||
setIsComposing(false);
|
||||
referenceChange(event);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
const BASE_CODE = 44032;
|
||||
const CHO = 588;
|
||||
const JUNG = 28;
|
||||
|
||||
const cho = ["ㄱ", "ㄲ", "ㄴ", "ㄷ", "ㄸ", "ㄹ", "ㅁ", "ㅂ", "ㅃ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅉ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"];
|
||||
const jung = ["ㅏ", "ㅐ", "ㅑ", "ㅒ", "ㅓ", "ㅔ", "ㅕ", "ㅖ", "ㅗ", "ㅘ", "ㅙ", "ㅚ", "ㅛ", "ㅜ", "ㅝ", "ㅞ", "ㅟ", "ㅠ", "ㅡ", "ㅢ", "ㅣ"];
|
||||
const jong = ["", "ㄱ", "ㄲ", "ㄳ", "ㄴ", "ㄵ", "ㄶ", "ㄷ", "ㄹ", "ㄺ", "ㄻ", "ㄼ", "ㄽ", "ㄾ", "ㄿ", "ㅀ", "ㅁ", "ㅂ", "ㅄ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ"];
|
||||
|
||||
// Decompose the medial vowel into individual components, if necessary
|
||||
const jungSplit = {
|
||||
"ㅘ": ["ㅗ", "ㅏ"],
|
||||
"ㅙ": ["ㅗ", "ㅐ"],
|
||||
"ㅚ": ["ㅗ", "ㅣ"],
|
||||
"ㅝ": ["ㅜ", "ㅓ"],
|
||||
"ㅞ": ["ㅜ", "ㅔ"],
|
||||
"ㅟ": ["ㅜ", "ㅣ"],
|
||||
"ㅢ": ["ㅡ", "ㅣ"],
|
||||
};
|
||||
|
||||
// decompose ending consonents
|
||||
const jongSplit = {
|
||||
"ㄲ": ["ㄱ", "ㄱ"],
|
||||
"ㄳ": ["ㄱ", "ㅅ"],
|
||||
"ㄵ": ["ㄴ", "ㅈ"],
|
||||
"ㄶ": ["ㄴ", "ㅎ"],
|
||||
"ㄺ": ["ㄹ", "ㄱ"],
|
||||
"ㄻ": ["ㄹ", "ㅁ"],
|
||||
"ㄼ": ["ㄹ", "ㅂ"],
|
||||
"ㄽ": ["ㄹ", "ㅅ"],
|
||||
"ㄾ": ["ㄹ", "ㅌ"],
|
||||
"ㄿ": ["ㄹ", "ㅍ"],
|
||||
"ㅀ": ["ㄹ", "ㅎ"],
|
||||
"ㅄ": ["ㅂ", "ㅅ"],
|
||||
"ㅆ": ["ㅅ", "ㅅ"],
|
||||
}
|
||||
|
||||
export function decomposeHangul(character) {
|
||||
const code = character.charCodeAt(0) - BASE_CODE;
|
||||
|
||||
if (code < 0 || code > 11171) {
|
||||
// Return character as is if it's not a Hangul syllable
|
||||
return character;
|
||||
}
|
||||
|
||||
const choIndex = Math.floor(code / CHO);
|
||||
const jungIndex = Math.floor((code - (choIndex * CHO)) / JUNG);
|
||||
const jongIndex = code % JUNG;
|
||||
|
||||
// Decompose cho, jung, and jong
|
||||
const choJamo = cho[choIndex];
|
||||
const jungJamo = jung[jungIndex];
|
||||
const jongJamo = jong[jongIndex];
|
||||
|
||||
// Handle double jamo in jung
|
||||
const jungComponents = jungSplit[jungJamo] || [jungJamo];
|
||||
|
||||
|
||||
// handle double jamo in jong
|
||||
const jongComponents = jongSplit[jongJamo] || [jongJamo];
|
||||
|
||||
return [choJamo, ...jungComponents, ...jongComponents].join('');
|
||||
}
|
||||
|
||||
export function decomposeStringToJamo(inputString) {
|
||||
return inputString.split('').map(decomposeHangul).join('');
|
||||
}
|
||||
|
||||
export function jamoSubstringMatch(mainString, substring) {
|
||||
const decomposedMain = decomposeStringToJamo(mainString)
|
||||
.replace(/\s+/g, "");
|
||||
const decomposedSub = decomposeStringToJamo(substring)
|
||||
.replace(/\s+/g, "");
|
||||
|
||||
return decomposedMain.startsWith(decomposedSub);
|
||||
}
|
||||
|
||||
// Unicode Ranges for Korean Characters:
|
||||
// Hangul Syllables: \uAC00-\uD7A3
|
||||
// Hangul Jamo: \u1100-\u11FF (for initial consonants, vowels, and final consonants)
|
||||
// Hangul Compatibility Jamo: \u3130-\u318F
|
||||
export function containsKorean(text) {
|
||||
const koreanRegex = /[\u1100-\u11FF\u3130-\u318F\uAC00-\uD7A3]/;
|
||||
return koreanRegex.test(text);
|
||||
}
|
Loading…
Reference in New Issue