UNPKG

qwerty-dubeolsik

Version:

⌨ This library allows you to easily convert Korean keys to English or English keys to Korean on a QWERTY keyboard.

416 lines (403 loc) 9.97 kB
/** * unicodeKoreanChars * [!] 키 = 분리된 문자(NFKD), 값 = 완성된 문자 * [!] key = decomposed character(NFKD), value = composed character */ export const unicodeKoreanChars: Record<string, string> = { ᄇ: "ㅂ", ᄌ: "ㅈ", ᄃ: "ㄷ", ᄀ: "ㄱ", ᄉ: "ㅅ", ᅭ: "ㅛ", ᅧ: "ㅕ", ᅣ: "ㅑ", ᅢ: "ㅐ", ᅦ: "ㅔ", ᄆ: "ㅁ", ᄂ: "ㄴ", ᄋ: "ㅇ", ᄅ: "ㄹ", ᄒ: "ㅎ", ᅩ: "ㅗ", ᅥ: "ㅓ", ᅡ: "ㅏ", ᅵ: "ㅣ", ᄏ: "ㅋ", ᄐ: "ㅌ", ᄎ: "ㅊ", ᄑ: "ㅍ", ᅲ: "ㅠ", ᅮ: "ㅜ", ᅳ: "ㅡ", ᄈ: "ㅃ", ᄍ: "ㅉ", ᄄ: "ㄸ", ᄁ: "ㄲ", ᄊ: "ㅆ", ᅤ: "ㅒ", ᅨ: "ㅖ", ᅪ: "ㅘ", ᅫ: "ㅙ", ᅬ: "ㅚ", ᅯ: "ㅝ", ᅰ: "ㅞ", ᅱ: "ㅟ", ᅴ: "ㅢ", // 종성 ᆨ: "ㄱ", ᆩ: "ㄲ", ᆫ: "ㄴ", ᆮ: "ㄷ", ᆯ: "ㄹ", ᆷ: "ㅁ", ᆸ: "ㅂ", ᆺ: "ㅅ", ᆻ: "ㅆ", ᆼ: "ㅇ", ᇂ: "ㅎ", ᆽ: "ㅈ", ᆾ: "ㅊ", ᆿ: "ㅋ", ᇀ: "ㅊ", ᇁ: "ㅍ", ㄸ: "ㄸ", ㅉ: "ㅉ", } as const; /** * qwertyKorKeys */ export const qwertyKorKeys: Record<string, string> = { q: "ᄇ", w: "ᄌ", e: "ᄃ", r: "ᄀ", t: "ᄉ", y: "ᅭ", u: "ᅧ", i: "ᅣ", o: "ᅢ", p: "ᅦ", a: "ᄆ", s: "ᄂ", d: "ᄋ", f: "ᄅ", g: "ᄒ", h: "ᅩ", j: "ᅥ", k: "ᅡ", l: "ᅵ", z: "ᄏ", x: "ᄐ", c: "ᄎ", v: "ᄑ", b: "ᅲ", n: "ᅮ", m: "ᅳ", Q: "ᄈ", W: "ᄍ", E: "ᄄ", R: "ᄁ", T: "ᄊ", O: "ᅤ", P: "ᅨ", } as const; /** * qwertyEngKeys */ export const qwertyEngKeys: Record<string, string> = Object.entries( qwertyKorKeys, ).reduce( (acc, [key, value]) => { acc[value] = key; return acc; }, {} as Record<string, string>, ); /** * initialConsonants * [!] 키 = 종성, 값 = 초성 * [!] key = final consonant, value = initial consonant */ export const initialConsonants: Record<string, string> = { ᆨ: "ᄀ", ᆩ: "ᄁ", ᆫ: "ᄂ", ᆮ: "ᄃ", ᆯ: "ᄅ", ᆷ: "ᄆ", ᆸ: "ᄇ", ᆺ: "ᄉ", ᆻ: "ᄊ", ᆼ: "ᄋ", ᄒ: "ᇂ", ᆽ: "ᄌ", ᆾ: "ᄎ", ᆿ: "ᄏ", ᇀ: "ᄐ", ᇁ: "ᄑ", ㄸ: "ᄄ", ㅉ: "ᄍ", ᄈ: "", } as const; /** * finalConsonants * [!] 키 = 초성, 값 = 종성 * [!] key = initial consonant, value = final consonant */ export const finalConsonants: Record<string, string> = Object.entries( initialConsonants, ).reduce( (acc, [key, value]) => { acc[value] = key; return acc; }, {} as Record<string, string>, ); /** * composedFinalConsonants * [!] 키 = 초성, 값 = 종성 * [!] key = initial consonant, value = final consonant */ const composedFinalConsonants: Record<string, string> = { ᄀᄉ: "ᆳ", ᄂᄌ: "ᆬ", ᄂᄒ: "ᆭ", ᄅᄀ: "ᆰ", ᄅᄆ: "ᆱ", ᄅᄇ: "ᆲ", ᄅᄉ: "ᆳ", ᄅᄐ: "ᆴ", ᄅᄑ: "ᆵ", ᄅᄒ: "ᆶ", ᄇᄉ: "ᆹ", }; /** * decomposedFinalConsonants * [!] 키 = 종성, 값 = 초성 * [!] key = final consonant, value = initial consonant */ const decomposedFinalConsonants: Record<string, string> = Object.entries( composedFinalConsonants, ).reduce( (acc, [key, value]) => { acc[value] = key; return acc; }, {} as Record<string, string>, ); /** * diphthongs */ const diphthongs: Record<string, string> = { ᅩᅡ: "ᅪ", ᅩᅢ: "ᅫ", ᅩᅵ: "ᅬ", ᅮᅥ: "ᅯ", ᅮᅦ: "ᅰ", ᅮᅵ: "ᅱ", ᅳᅵ: "ᅴ", }; /** * decomposedVowels */ const decomposedVowels: Record<string, string> = Object.entries( diphthongs, ).reduce( (acc, [key, value]) => { acc[value] = key; return acc; }, {} as Record<string, string>, ); /** * checkConsonant */ export const checkConsonant = (char: string) => { return char in finalConsonants || char in initialConsonants; }; /** * convertEngKeyToKorKey */ export const convertEngKeyToKorKey = (engChar: string) => { if (engChar in qwertyKorKeys) { return qwertyKorKeys[engChar]; } if (engChar.toLowerCase() in qwertyKorKeys) { return qwertyKorKeys[engChar.toLowerCase()]; } if (engChar.toUpperCase() in qwertyKorKeys) { return qwertyKorKeys[engChar.toUpperCase()]; } return engChar; }; /** * convertEngToKor */ export const convertEngToKor = (engString: string) => { const ret = []; for (let i = 0; i < engString.length; i += 1) { const engChar = engString[i]; let isConsonantPrevChar = false; let isConsonantNextChar = false; if (i > 0) { const prevChar = engString[i - 1]; const prevKorChar = convertEngKeyToKorKey(prevChar); isConsonantPrevChar = checkConsonant(prevKorChar); } if (engString.length - 1 !== i) { const nextChar = engString[i + 1]; const nextKorChar = convertEngKeyToKorKey(nextChar); isConsonantNextChar = checkConsonant(nextKorChar); } const currKorChar = convertEngKeyToKorKey(engChar); const isConsonantCurrChar = checkConsonant(currKorChar); if ( isConsonantCurrChar === false && isConsonantNextChar === false && engString.length - 1 > i ) { /** * 현재 문자와 다음 문자가 자음이 아닌 경우 복합 종성 처리 * If the current and next characters are not consonants, process the composed final consonant */ const nextChar = engString[i + 1]; const nextKorChar = convertEngKeyToKorKey(nextChar); const result = diphthongs[currKorChar + nextKorChar]; if (result) { ret.push(result); i += 1; } else { /** * 복합 중성으로 처리되지 않은 경우 * If not processed as a composed vowel */ ret.push(currKorChar); } } else if ( isConsonantPrevChar === false && isConsonantCurrChar && isConsonantNextChar && i > 0 ) { /** * 이전 문자가 자음이 아니고, 현재, 다음 문자가 모두 자음인 경우 복합 종성으로 변경 * If the previous character is not a consonant and the current and next characters are all consonants, change to a composed final consonant */ const nextChar = engString[i + 1]; const nextKorChar = convertEngKeyToKorKey(nextChar); const complexFinalConsonant = composedFinalConsonants[currKorChar + nextKorChar]; if (complexFinalConsonant) { ret.push(complexFinalConsonant); /** * 다음 문자를 처리하지 않음(현재 문자 + 다음 문자 = 복합 종성처리) * Do not process the next character (current character + next character = composed final consonant processing) */ i += 1; } else { /** * 복합 종성으로 변경되지 않은 경우 종성 처리 * If not changed to a composed final consonant, process the final consonant */ ret.push(finalConsonants[currKorChar] || currKorChar); } } else if (i === engString.length - 1 && isConsonantCurrChar) { /** * 현재 문자가 초성이고 마지막 문자인 경우 종성으로 변경 * If the current character is an initial consonant and the last character, change to a final consonant */ ret.push(finalConsonants[currKorChar] || currKorChar); } else { /** * 그 외의 경우 * Other cases */ ret.push(currKorChar); } } /** * 한글 문자열로 변환 * Convert to Korean string */ const outputString = ret.join("").normalize("NFC"); /** * 완성형 문자중 누락된 한글 자음 또는 모음을 원래 문자열로 매핑 * Map missing Korean consonants or vowels in composed string to original string */ return [...outputString] .map((char) => unicodeKoreanChars[char] || char) .join(""); }; /** * convertKorToEng */ export const convertKorToEng = (korString: string) => { const ret: string[] = []; const decomposedKorCharList = korString.normalize("NFKD").split(""); const korCharList: string[] = []; decomposedKorCharList.forEach((decomposedChar) => { /** * 복합 문자열 변환 처리 */ if (decomposedChar in decomposedVowels) { decomposedVowels[decomposedChar].split("").forEach((char) => { korCharList.push(char); }); } else { korCharList.push(decomposedChar); } }); for (let i = 0; i < korCharList.length; i += 1) { const currKorChar = korCharList[i]; if (currKorChar in qwertyEngKeys) { /** * 쿼티 매핑 * QWERTY mapping */ ret.push(qwertyEngKeys[currKorChar]); } else if (currKorChar in initialConsonants) { /** * 초성 처리 * Initial consonants */ const key = initialConsonants[currKorChar]; ret.push(qwertyEngKeys[key]); } else if (currKorChar in decomposedFinalConsonants) { /** * 복합 종성 처리 * Composed final consonants */ const [firstChar, secondChar] = decomposedFinalConsonants[currKorChar].split(""); ret.push(qwertyEngKeys[firstChar]); ret.push(qwertyEngKeys[secondChar]); } else if (currKorChar in decomposedVowels) { /** * 복합 모음 처리 * Composed vowels */ const [firstChar, secondChar] = decomposedVowels[currKorChar].split(""); ret.push(qwertyEngKeys[firstChar]); ret.push(qwertyEngKeys[secondChar]); } else { /** * 그 외의 경우 * Other cases */ ret.push(currKorChar); } } /** * 완성형 문자열로 변환 * Convert to composed string */ const outputString = ret.join("").normalize("NFC"); /** * 완성형 문자중 누락된 한글 자음 또는 모음을 원래 문자열로 매핑 * Map missing Korean consonants or vowels in composed string to original string */ return [...outputString] .map((char) => unicodeKoreanChars[char] || char) .join(""); };