368 lines
12 KiB
Lua
368 lines
12 KiB
Lua
--- Eng char typed as korean keyboard conversion
|
||
--- source : https://blog.naver.com/milennium9/20205300449
|
||
|
||
local compConvertTable = {
|
||
{12593,4352}, -- ᄀ' == 'ㄱ'
|
||
{12594,4353}, -- ᄁ' == 'ㄲ'
|
||
{12596,4354}, -- ᄂ' == 'ㄴ'
|
||
{12599,4355}, -- ᄃ' == 'ㄷ'
|
||
{12600,4356}, -- ᄄ' == 'ㄸ'
|
||
{12601,4357}, -- ᄅ' == 'ㄹ'
|
||
{12609,4358}, -- ᄆ' == 'ㅁ'
|
||
{12610,4359}, -- ᄇ' == 'ㅂ'
|
||
{12611,4360}, -- ᄈ' == 'ㅃ'
|
||
{12613,4361}, -- ᄉ' == 'ㅅ'
|
||
{12614,4362}, -- ᄊ' == 'ㅆ'
|
||
{12615,4363}, -- ᄋ' == 'ㅇ'
|
||
{12616,4364}, -- ᄌ' == 'ㅈ'
|
||
{12617,4365}, -- ᄍ' == 'ㅉ'
|
||
{12618,4366}, -- ᄎ' == 'ㅊ'
|
||
{12619,4367}, -- ᄏ' == 'ㅋ'
|
||
{12620,4368}, -- ᄐ' == 'ㅌ'
|
||
{12621,4369}, -- ᄑ' == 'ㅍ'
|
||
{12622,4370}, -- ᄒ' == 'ㅎ'
|
||
{12645,4372}, -- ᄔ' == 'ㅥ'
|
||
{12646,4373}, -- ᄕ' == 'ㅦ'
|
||
{12653,4378}, -- ᄚ' == 'ㅭ'
|
||
{12654,4380}, -- ᄜ' == 'ㅮ'
|
||
{12657,4381}, -- ᄝ' == 'ㅱ'
|
||
{12658,4382}, -- ᄞ' == 'ㅲ'
|
||
{12659,4384}, -- ᄠ' == 'ㅳ'
|
||
{12612,4385}, -- ᄡ' == 'ㅄ'
|
||
{12660,4386}, -- ᄢ' == 'ㅴ'
|
||
{12661,4387}, -- ᄣ' == 'ㅵ'
|
||
{12662,4391}, -- ᄧ' == 'ㅶ'
|
||
{12663,4393}, -- ᄩ' == 'ㅷ'
|
||
{12664,4395}, -- ᄫ' == 'ㅸ'
|
||
{12665,4396}, -- ᄬ' == 'ㅹ'
|
||
{12666,4397}, -- ᄭ' == 'ㅺ'
|
||
{12667,4398}, -- ᄮ' == 'ㅻ'
|
||
{12668,4399}, -- ᄯ' == 'ㅼ'
|
||
{12669,4402}, -- ᄲ' == 'ㅽ'
|
||
{12670,4406}, -- ᄶ' == 'ㅾ'
|
||
{12613,4412}, -- ᄼ' == 'ㅅ'
|
||
{12614,4413}, -- ᄽ' == 'ㅆ'
|
||
{12613,4414}, -- ᄾ' == 'ㅅ'
|
||
{12614,4415}, -- ᄿ' == 'ㅆ'
|
||
{12671,4416}, -- ᅀ' == 'ㅿ'
|
||
{12674,4421}, -- ᅅ' == 'ㆂ'
|
||
{12675,4422}, -- ᅆ' == 'ㆃ'
|
||
{12672,4423}, -- ᅇ' == 'ㆀ'
|
||
{12615,4428}, -- ᅌ' == 'ㅇ'
|
||
{12616,4430}, -- ᅎ' == 'ㅈ'
|
||
{12617,4431}, -- ᅏ' == 'ㅉ'
|
||
{12616,4432}, -- ᅐ' == 'ㅈ'
|
||
{12617,4433}, -- ᅑ' == 'ㅉ'
|
||
{12618,4436}, -- ᅔ' == 'ㅊ'
|
||
{12618,4437}, -- ᅕ' == 'ㅊ'
|
||
{12676,4439}, -- ᅗ' == 'ㆄ'
|
||
{12677,4440}, -- ᅘ' == 'ㆅ'
|
||
{12678,4441}, -- ᅙ' == 'ㆆ'
|
||
{12644,4447}, -- ᅟ' == 'ㅤ'
|
||
{12623,4449}, -- ᅡ' == 'ㅏ'
|
||
{12624,4450}, -- ᅢ' == 'ㅐ'
|
||
{12625,4451}, -- ᅣ' == 'ㅑ'
|
||
{12626,4452}, -- ᅤ' == 'ㅒ'
|
||
{12627,4453}, -- ᅥ' == 'ㅓ'
|
||
{12628,4454}, -- ᅦ' == 'ㅔ'
|
||
{12629,4455}, -- ᅧ' == 'ㅕ'
|
||
{12630,4456}, -- ᅨ' == 'ㅖ'
|
||
{12631,4457}, -- ᅩ' == 'ㅗ'
|
||
{12632,4458}, -- ᅪ' == 'ㅘ'
|
||
{12633,4459}, -- ᅫ' == 'ㅙ'
|
||
{12634,4460}, -- ᅬ' == 'ㅚ'
|
||
{12635,4461}, -- ᅭ' == 'ㅛ'
|
||
{12636,4462}, -- ᅮ' == 'ㅜ'
|
||
{12637,4463}, -- ᅯ' == 'ㅝ'
|
||
{12638,4464}, -- ᅰ' == 'ㅞ'
|
||
{12639,4465}, -- ᅱ' == 'ㅟ'
|
||
{12640,4466}, -- ᅲ' == 'ㅠ'
|
||
{12641,4467}, -- ᅳ' == 'ㅡ'
|
||
{12642,4468}, -- ᅴ' == 'ㅢ'
|
||
{12643,4469}, -- ᅵ' == 'ㅣ'
|
||
{12679,4484}, -- ᆄ' == 'ㆇ'
|
||
{12680,4485}, -- ᆅ' == 'ㆈ'
|
||
{12681,4488}, -- ᆈ' == 'ㆉ'
|
||
{12682,4497}, -- ᆑ' == 'ㆊ'
|
||
{12683,4498}, -- ᆒ' == 'ㆋ'
|
||
{12684,4500}, -- ᆔ' == 'ㆌ'
|
||
{12685,4510}, -- ᆞ' == 'ㆍ'
|
||
{12686,4513}, -- ᆡ' == 'ㆎ'
|
||
{12593,4520}, -- ᆨ' == 'ㄱ'
|
||
{12594,4521}, -- ᆩ' == 'ㄲ'
|
||
{12595,4522}, -- ᆪ' == 'ㄳ'
|
||
{12596,4523}, -- ᆫ' == 'ㄴ'
|
||
{12597,4524}, -- ᆬ' == 'ㄵ'
|
||
{12598,4525}, -- ᆭ' == 'ㄶ'
|
||
{12599,4526}, -- ᆮ' == 'ㄷ'
|
||
{12601,4527}, -- ᆯ' == 'ㄹ'
|
||
{12602,4528}, -- ᆰ' == 'ㄺ'
|
||
{12603,4529}, -- ᆱ' == 'ㄻ'
|
||
{12604,4530}, -- ᆲ' == 'ㄼ'
|
||
{12605,4531}, -- ᆳ' == 'ㄽ'
|
||
{12606,4532}, -- ᆴ' == 'ㄾ'
|
||
{12607,4533}, -- ᆵ' == 'ㄿ'
|
||
{12608,4534}, -- ᆶ' == 'ㅀ'
|
||
{12609,4535}, -- ᆷ' == 'ㅁ'
|
||
{12610,4536}, -- ᆸ' == 'ㅂ'
|
||
{12612,4537}, -- ᆹ' == 'ㅄ'
|
||
{12613,4538}, -- ᆺ' == 'ㅅ'
|
||
{12614,4539}, -- ᆻ' == 'ㅆ'
|
||
{12615,4540}, -- ᆼ' == 'ㅇ'
|
||
{12616,4541}, -- ᆽ' == 'ㅈ'
|
||
{12618,4542}, -- ᆾ' == 'ㅊ'
|
||
{12619,4543}, -- ᆿ' == 'ㅋ'
|
||
{12620,4544}, -- ᇀ' == 'ㅌ'
|
||
{12621,4545}, -- ᇁ' == 'ㅍ'
|
||
{12622,4546}, -- ᇂ' == 'ㅎ'
|
||
{12646,4550}, -- ᇆ' == 'ㅦ'
|
||
{12647,4551}, -- ᇇ' == 'ㅧ'
|
||
{12648,4552}, -- ᇈ' == 'ㅨ'
|
||
{12649,4556}, -- ᇌ' == 'ㅩ'
|
||
{12650,4558}, -- ᇎ' == 'ㅪ'
|
||
{12651,4563}, -- ᇓ' == 'ㅫ'
|
||
{12652,4567}, -- ᇗ' == 'ㅬ'
|
||
{12653,4569}, -- ᇙ' == 'ㅭ'
|
||
{12654,4572}, -- ᇜ' == 'ㅮ'
|
||
{12655,4573}, -- ᇝ' == 'ㅯ'
|
||
{12656,4575}, -- ᇟ' == 'ㅰ'
|
||
{12664,4582}, -- ᇦ' == 'ㅸ'
|
||
{12666,4583}, -- ᇧ' == 'ㅺ'
|
||
{12668,4584}, -- ᇨ' == 'ㅼ'
|
||
{12669,4586}, -- ᇪ' == 'ㅽ'
|
||
{12671,4587}, -- ᇫ' == 'ㅿ'
|
||
{12672,4590}, -- ᇮ' == 'ㆀ'
|
||
{12615,4592}, -- ᇰ' == 'ㅇ'
|
||
{12674,4593}, -- ᇱ' == 'ㆂ'
|
||
{12675,4594}, -- ᇲ' == 'ㆃ'
|
||
{12676,4596}, -- ᇴ' == 'ㆄ'
|
||
{12678,4601}, -- ᇹ' == 'ㆆ'
|
||
}
|
||
|
||
-- Convert Hangul Jamo to Hangul Compatibility Jamo code
|
||
-- - https://en.wikipedia.org/wiki/Hangul_Compatibility_Jamo
|
||
-- - https://en.wikipedia.org/wiki/Hangul_Jamo_(Unicode_block)
|
||
-- - There is no calculation for this, so we need to convert this code manually
|
||
local function jamoToCompJamo(code)
|
||
for _, e in ipairs(compConvertTable) do
|
||
if code == e[2] then
|
||
return e[1]
|
||
end
|
||
end
|
||
return 0
|
||
end
|
||
|
||
---@param tt "initial"|"medial"|"final"|"partialOnly"
|
||
---@param char string
|
||
---@return integer UnicodeChar
|
||
local function getCode( tt, char )
|
||
-- 초성 리스트 (initial list)
|
||
local initial = { 'r','R','s','e','E','f','a','q','Q','t','T','d','w','W','c','z','x','v','g'};
|
||
-- 중성 리스트 (medial list)
|
||
local medial = { 'k', 'o', 'i', 'O', 'j', 'p', 'u', 'P', 'h', 'hk', 'ho', 'hl', 'y', 'n', 'nj', 'np', 'nl', 'b', 'm', 'ml', 'l' };
|
||
-- 종성 리스트 (final list)
|
||
local final = { 'r', 'R', 'rt', 's', 'sw', 'sg', 'e', 'f', 'fr', 'fa', 'fq', 'ft', 'fx', 'fv', 'fg', 'a', 'q', 'qt', 't', 'T', 'd', 'w', 'c', 'z', 'x', 'v', 'g' };
|
||
local returnCode = 0; -- 최종 인덱스 (return index)
|
||
local isFind = false; -- 발견 여부 (code found check)
|
||
|
||
-- 각 리스트에서 인덱스를 검색하기 위한 로컬 함수
|
||
local find = function(tb, ch)
|
||
for i = 1, #tb do
|
||
if tb[i] == ch then
|
||
returnCode = i-1;
|
||
isFind = true;
|
||
break;
|
||
end
|
||
end
|
||
end
|
||
|
||
if char ~= nil then
|
||
if tt == 'initial' then
|
||
find( initial, char )
|
||
returnCode = returnCode * 21 * 28
|
||
elseif tt == 'medial' then
|
||
find( medial, char )
|
||
returnCode = returnCode * 28
|
||
elseif tt == 'final' then
|
||
find( final, char )
|
||
returnCode = returnCode + 1
|
||
end
|
||
end
|
||
if isFind == false then returnCode = -1; end
|
||
return returnCode;
|
||
end
|
||
|
||
---Converts 0x1101 like unicode to string
|
||
---@param unicode_list table
|
||
---@return string convertedUnicodeChar
|
||
local function conv2utf8(unicode_list)
|
||
local result = ''
|
||
local w,x,y,z = 0,0,0,0
|
||
local function modulo(a, b)
|
||
return a - math.floor(a/b) * b
|
||
end
|
||
for _,v in ipairs(unicode_list) do
|
||
if v ~= 0 and v ~= nil then
|
||
if utf8 then
|
||
-- Tested in REPENTOGON
|
||
result = result .. utf8.char(v)
|
||
else
|
||
-- Assume Lua 5.3 not supporting utf8
|
||
if v <= 0x7F then -- same as ASCII
|
||
result = result .. string.char(v)
|
||
elseif v >= 0x80 and v <= 0x7FF then -- 2 bytes
|
||
y = math.floor(modulo(v, 0x000800) / 64)
|
||
z = modulo(v, 0x000040)
|
||
result = result .. string.char(0xC0 + y, 0x80 + z)
|
||
elseif (v >= 0x800 and v <= 0xD7FF) or (v >= 0xE000 and v <= 0xFFFF) then -- 3 bytes
|
||
x = math.floor(modulo(v, 0x010000) / 4096)
|
||
y = math.floor(modulo(v, 0x001000) / 64)
|
||
z = modulo(v, 0x000040)
|
||
result = result .. string.char(0xE0 + x, 0x80 + y, 0x80 + z)
|
||
elseif (v >= 0x10000 and v <= 0x10FFFF) then -- 4 bytes
|
||
w = math.floor(modulo(v, 0x200000) / 262144)
|
||
x = math.floor(modulo(v, 0x040000) / 4096)
|
||
y = math.floor(modulo(v, 0x001000) / 64)
|
||
z = modulo(v, 0x000040)
|
||
result = result .. string.char(0xF0 + w, 0x80 + x, 0x80 + y, 0x80 + z)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
return result
|
||
end
|
||
|
||
---converts single Korean word within string and index
|
||
---@param text string
|
||
---@param idx integer
|
||
---@return string convertedKoreanWord
|
||
---@return integer newIndex
|
||
local function getFinalString(text, idx)
|
||
|
||
local idxAppend = 0
|
||
local res = ""
|
||
|
||
-- 한글의 초,중,종성의 인덱스
|
||
-- Initial, Medial, Final index
|
||
local initialCode = 0;
|
||
local medialCode = 0;
|
||
local finalCode = 0;
|
||
|
||
-- 초성 검색 (Initial char check)
|
||
local first = text:sub( idx, idx )
|
||
local firstP1 = text:sub( idx, idx+1 )
|
||
initialCode = getCode( 'initial', first );
|
||
local tempMedialCodeP1 = getCode( 'medial', firstP1 );
|
||
local tempMedialCodeP2 = getCode( 'medial', first );
|
||
if tempMedialCodeP1 ~= -1 then
|
||
-- Medial came first
|
||
-- Medial char starts with 0x1161, 0x3131
|
||
local code = jamoToCompJamo(0x1161 + (tempMedialCodeP1 // (1 * 28)))
|
||
local ch = conv2utf8({code})
|
||
return ch, idx + 2
|
||
elseif tempMedialCodeP2 ~= -1 then
|
||
-- Medial came first
|
||
-- Medial char starts with 0x1161, 0x3131
|
||
local code = jamoToCompJamo(0x1161 + (tempMedialCodeP2 // (1 * 28)))
|
||
local ch = conv2utf8({code})
|
||
return ch, idx + 1
|
||
elseif initialCode < 0 then
|
||
-- This isn't Korean character, returning orignal char
|
||
return first, idx + 1
|
||
end
|
||
|
||
res = res .. first
|
||
idxAppend = 1
|
||
|
||
-- 중성 검색 (Medial char check)
|
||
local secondP1 = text:sub( idx+1, idx+2 )
|
||
local secondP2 = text:sub( idx+1, idx+1 )
|
||
local medialCodeP1 = getCode( 'medial', secondP1 ); -- 2 chars of medial position
|
||
local medialCodeP2 = getCode( 'medial', secondP2 ); -- 1 char of medial position
|
||
--local medialCodeP3 = getCode( 'initial', secondP2 ); -- 1 char of medial position, but check for initial char
|
||
|
||
if medialCodeP1 ~= -1 then
|
||
-- 2 word medials
|
||
res = res .. secondP1
|
||
medialCode = medialCodeP1
|
||
idxAppend = idxAppend + 2
|
||
elseif medialCodeP2 ~= -1 then
|
||
-- 1 word medial
|
||
res = res .. secondP2
|
||
medialCode = medialCodeP2
|
||
idxAppend = idxAppend + 1
|
||
else
|
||
-- no medials, returning initial char
|
||
-- initial char starts with 0x1100
|
||
local code = jamoToCompJamo(0x1100 + (initialCode // (1 * 21 * 28)))
|
||
local ch = conv2utf8({code})
|
||
return ch, idx + 1
|
||
end
|
||
|
||
-- 종성 검색 (Final char check)
|
||
local finalP1 = text:sub( idx+idxAppend, idx+idxAppend+1 )
|
||
local finalP2 = text:sub( idx+idxAppend, idx+idxAppend )
|
||
local finalP3 = text:sub( idx+idxAppend+1, idx+idxAppend+1 )
|
||
local finalCodeP1 = getCode( 'final', finalP1 ); -- 2 chars of final position
|
||
local finalCodeP2 = getCode( 'final', finalP2 ); -- 1 char of final position
|
||
local finalCodeP3 = getCode( 'medial', finalP3 ); -- 1 char of final position, but check for medial char
|
||
|
||
if finalCodeP1 ~= -1 then
|
||
-- 2 word finals
|
||
local nextIndex = idx + idxAppend + 2
|
||
-- check initial position for next word
|
||
local tempExtraMedialCode = getCode( 'medial', text:sub( nextIndex, nextIndex ) );
|
||
if tempExtraMedialCode ~= -1 or finalP1 == finalP2 then
|
||
-- medial was found in initial position. 1 word final is considered
|
||
finalCode = finalCodeP2
|
||
idxAppend = idxAppend + 1
|
||
else
|
||
-- 2 word finals truly
|
||
finalCode = finalCodeP1
|
||
idxAppend = idxAppend + 2
|
||
end
|
||
else
|
||
-- 0~1 word finals
|
||
if finalCodeP3 ~= -1 then
|
||
-- medial was found in final position. no word final is considered
|
||
finalCode = 0;
|
||
elseif finalCodeP2 ~= -1 then
|
||
-- 1 word final truly
|
||
finalCode = finalCodeP2
|
||
idxAppend = idxAppend + 1
|
||
else
|
||
-- no final was found
|
||
finalCode = 0;
|
||
end
|
||
end
|
||
|
||
-- final return
|
||
local code = 0xAC00 + initialCode + medialCode + finalCode
|
||
local ch = conv2utf8({code})
|
||
return ch, idx + idxAppend
|
||
end
|
||
|
||
---Converts English typed string as Korean keyboard ('abc' returns 'ㅁㄴㅇ')
|
||
---@param msg string
|
||
---@return string
|
||
local function engToKor(msg)
|
||
if msg == nil then
|
||
return "nil"
|
||
end
|
||
|
||
local text = msg;
|
||
|
||
-- 루프와 결과 저장을 위한 변수
|
||
local textLength = #text
|
||
local ret = "";
|
||
local idx = 1
|
||
|
||
while idx <= textLength do
|
||
local ch, index = getFinalString(msg, idx)
|
||
ret = ret .. ch
|
||
idx = index
|
||
end
|
||
return ret
|
||
end
|
||
|
||
EID.engKeystrokeToKor = engToKor |