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 { useState } from "react";
|
||||||
import "./VerseValidator.css";
|
import "./VerseValidator.css";
|
||||||
import { StringDiff } from "react-string-diff";
|
import { StringDiff } from "react-string-diff";
|
||||||
|
import { containsKorean, jamoSubstringMatch } from './jamoUtils';
|
||||||
|
|
||||||
const STATE = {
|
const STATE = {
|
||||||
INCORRECT: 0,
|
INCORRECT: 0,
|
||||||
|
@ -23,11 +24,36 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
const[diffBool, setDiffBool] = useState(false)
|
const[diffBool, setDiffBool] = useState(false)
|
||||||
const [isComposing, setIsComposing] = useState(false);
|
const [isComposing, setIsComposing] = useState(false);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Handle the start of composition
|
// Handle the start of composition
|
||||||
const handleCompositionStart = () => {
|
const handleCompositionStart = () => {
|
||||||
setIsComposing(true);
|
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
|
// function to check correctness of reference input
|
||||||
const referenceChange = (e) => {
|
const referenceChange = (e) => {
|
||||||
|
@ -40,14 +66,8 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
.replace(/\s+/g, "")
|
.replace(/\s+/g, "")
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize("NFC");
|
.normalize("NFC");
|
||||||
var result = STATE.INCORRECT; // init
|
|
||||||
if (string1 === string2) {
|
const result = resultChecker(string1, string2);
|
||||||
result = STATE.CORRECT;
|
|
||||||
} else if (string2.startsWith(string1) & string1 !== "") {
|
|
||||||
result = STATE.PARTIAL
|
|
||||||
} else {
|
|
||||||
result = STATE.INCORRECT
|
|
||||||
}
|
|
||||||
|
|
||||||
setReference(value);
|
setReference(value);
|
||||||
setReferenceBool(result);
|
setReferenceBool(result);
|
||||||
|
@ -79,14 +99,9 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize("NFC");
|
.normalize("NFC");
|
||||||
|
|
||||||
var result = STATE.INCORRECT; // init
|
|
||||||
if (string1 === string2) {
|
const result = resultChecker(string1, string2);
|
||||||
result = STATE.CORRECT;
|
|
||||||
} else if (string2.startsWith(string1) & string1 !== "") {
|
|
||||||
result = STATE.PARTIAL
|
|
||||||
} else {
|
|
||||||
result = STATE.INCORRECT
|
|
||||||
}
|
|
||||||
|
|
||||||
setTitle(value);
|
setTitle(value);
|
||||||
setTitleBool(result);
|
setTitleBool(result);
|
||||||
|
@ -118,14 +133,7 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize("NFC");
|
.normalize("NFC");
|
||||||
|
|
||||||
var result = STATE.INCORRECT; // init
|
const result = resultChecker(string1, string2);
|
||||||
if (string1 === string2) {
|
|
||||||
result = STATE.CORRECT;
|
|
||||||
} else if (string2.startsWith(string1) & string1 !== "") {
|
|
||||||
result = STATE.PARTIAL
|
|
||||||
} else {
|
|
||||||
result = STATE.INCORRECT
|
|
||||||
}
|
|
||||||
|
|
||||||
setChapterTitle(value);
|
setChapterTitle(value);
|
||||||
setChapterTitleBool(result);
|
setChapterTitleBool(result);
|
||||||
|
@ -153,14 +161,7 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.normalize("NFC");
|
.normalize("NFC");
|
||||||
|
|
||||||
var result = STATE.INCORRECT; // init
|
const result = resultChecker(string1, string2);
|
||||||
if (string1 === string2) {
|
|
||||||
result = STATE.CORRECT;
|
|
||||||
} else if (string2.startsWith(string1) & string1 !== "") {
|
|
||||||
result = STATE.PARTIAL
|
|
||||||
} else {
|
|
||||||
result = STATE.INCORRECT
|
|
||||||
}
|
|
||||||
|
|
||||||
setVerse(value);
|
setVerse(value);
|
||||||
setVerseBool(result);
|
setVerseBool(result);
|
||||||
|
@ -215,15 +216,11 @@ const VerseValidator = ({ element: { pack, title, chapterTitle, reference, verse
|
||||||
id="referenceBox"
|
id="referenceBox"
|
||||||
name="referenceBox"
|
name="referenceBox"
|
||||||
onChange={(event) => {
|
onChange={(event) => {
|
||||||
|
|
||||||
if (!isComposing) {
|
if (!isComposing) {
|
||||||
referenceChange(event);
|
referenceChange(event);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onCompositionStart={handleCompositionStart}
|
|
||||||
onCompositionEnd={(event) => {
|
|
||||||
setIsComposing(false);
|
|
||||||
referenceChange(event);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</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