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
text/typescript
/**
* 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("");
};