디시인사이드 갤러리

갤러리 이슈박스, 최근방문 갤러리

갤러리 본문 영역

(초안, 기술 문서) 돌죽 한국어화 - 1

ASCIIPhilia갤로그로 이동합니다. 2025.04.22 01:59:07
조회 106 추천 8 댓글 9

https://gall.dcinside.com/board/view/?id=rlike&no=359290

 



[서론]


던전 크롤 수톤 수프의 한국어화는 한국어권 크롤 게이머들에게 오랜 숙원이었습니다. 지금까지 0.9, 0.21, 0.24 버전에서 한국어화 크롤이 만들어졌지만, 주로 다운로드판에서만 사용 가능했고, 유지관리되지 못하고 사라졌습니다. 그 이유는, 크롤의 다국어화가 너무 어렵기 때문입니다. 이 게임의 게임 메시지는 대부분 하드코딩 되어있습니다. 크롤은 C++ 코드 안에서 게임 메시지를 영어 문자열로 합성하며, 이는 번역이 편리하게 한 파일에 정리되어 있는 요즘의 상용 게임과는 아주 다릅니다. 개발진들의 노력에 의해서 일부 게임의 문자열들이 des/*.txt 파일로 정리되었지만, 이는 게임의 일부에 불과합니다. 지금까지의 한국어화 시도는 대부분 GNU gettext로 C++ 코드 내부의 문자열을 외부로 빼서 처리하는 것을 통해 이루어졌습니다. 다만, 이 방법은 한국어화 크롤이 오래 유지되지 못하는 원인이 되기도 했습니다. gettext로 추출된 텍스트는 어떠한 맥락에서 나온 것인지 판단할 수 없을 뿐더러, 크롤의 문자열 처리 로직에서 게임의 영향을 미치는 부분을 자동으로 고려할 수 없고, 그렇기에 한국어화 크롤은 오리지널 빌드와 다른 작동을 할 여지가 있습니다. 그렇기에 이 방법은 "우아하지 않은 방법"입니다. 이는 크롤의 메인 스트림에 해당 번역 방법을 결합시킬 수 없음을 의미하고, 매일 GitHub에서 트렁크 버전이 업데이트 되는 본 게임의 특성상, 유지보수가 되기 힘들었습니다. 그래서 버전이 올라가다 보면, 한국어화 크롤 메인테이너가 코드를 수정해야하는 품이 생기고, 점점 유지보수되지 않기 시작하다가 한국어화된 버전이 이어지지 않았습니다. 그렇다면 어떻게 해야할까요? 영어 문자열을 합성하는 크롤의 동적 문자열 생성 부분을 하드 코딩이 아니라 별도 파일을 통해 다국어화할 수 있게 정리하면 되지 않을까요? 물론 그것이 최선입니다. 하지만 대부분의 크롤 개발자들은 20년이 넘어가는 코드 베이스에서 이런 지루한 부분을 수정하기 보다는, 게임 요소를 추가하고 빼고 조정하는데 관심이 있어 보입니다. 또한 작업의 난이도도 엄청난 편입니다. 그렇기에 앞으로도 이 방식으로 다국어화가 지원될 가능성은 요원해보입니다. 


그렇다면 한국어화된 크롤을 유지가능하게 지속하려면 어떻게 해야할까요? 완벽하지는 않고 한계도 있지만 대안이 있습니다.


소스코드를 번역하는 바텀업 방식으로 접근하는게 아니라, 탑다운 방식으로 접근하는 것입니다. 즉, 최종 생성된 게임 메시지를 번역하는 것입니다.


크롤에는 웹타일 빌드가 있습니다. 이 버전에서는 크롤 바이너리가 웹 클라이언트랑 통신하기 위해서 웹소켓을 통해 JSON으로 게임 정보를 주고받습니다, 그리고 그 JSON에는 게임의 모든 텍스트가 담겨있습니다. 저는 비교적 최근에 크롤의 웹 클라이언트를 후킹하여 원하는 코드를 삽입할 수 있는 프레임워크(DWEM)을 만들었습니다. 이 프레임워크를 통하면, 크롤 바이너리가 전송한 JSON 메시지가 웹 클라이언트의 UI에 표시되기 전에 가로채서 수정이 가능합니다. 그리고 추가적인 프로젝트로, 해당 프레임워크를 이용해서 wtrec라고 하는 크롤 웹 타일을 리플레이하기 위한 포맷과 플레이어를 만들었습니다. wtrec 포맷의 구조는 단순합니다. 플레이어가 게임을 시작한 이례로 수신한 모든 게임 데이터 JSON 파일이 모아져 있습니다. 이걸 다시 웹 클라이언트에서 재생하기만 하면 리플레이가 이루어지는 것이죠. 그리고 제가 운영중인 CNC 서버에서는 작년 말부터 자동 녹화 봇이 일정 확률로 서버에서 플레이하는 플레이어들의 게임을 녹화했습니다. 그래서 지금, 200GB 상당의 가장 방대한 크롤 플레이 데이터가 모여있습니다. 오늘 이 데이터들을 파싱하여 번역할 텍스트를 추출하는 프로그램을 만들었습니다. 그리고 짬짬히 시간을 내어, 재귀적 정규표현식 처리를 이용하여 번역을 적용하고, 미번역 부분들을 수집하는 번역 엔진의 기초를 작성했습니다.


번역 엔진은 다음과 같이 작동합니다.


this.matchers = [{
        category: 'message',
        regex: {
            pattern: '(.+)',
            flags: ''
        },
        replaceValue: '$1',
        groups: ['hit_messages']
    },
    {
        category: 'hit_messages',
        regex: 'The rebounding (.+?) hits (.+?)\\.',
        replaceValue: '반동하는 {$1:이} {$2:를} 때렸다.',
        groups: ['magic', 'target']
    },
    {
        category: 'target',
        regex: 'manticore',
        replaceValue: '만티코어'
    },
    {
        category: 'target',
        regex: 'you',
        replaceValue: '당신',
    },
    {
        category: 'magic',
        regex: 'bolt of cold',
        replaceValue: '냉기 화살'
    }
]

this.functions = {
    '이': (a) => {
        // 복잡한 조사 처리 로직
        return a + '이'
    },
    '를': (a) => {
        // 복잡한 조사 처리 로직
        return a + '를'
    }
};
translate('The rebounding bolt of cold hits manticore.', 'korean', 'message')


위와 같이 matcher랑 functions를 작성합니다. matcher는 category, regex, replaceValue, group (선택적)을 가지는 객체입니다.

category = category:string

regex = regexString:string or {pattern:string, falgs:string}

replaceValue = string or {[language:string]: languageReplaceValue:string}

group = string[] (optional)


category는 matcher의 범주를 기술합니다.

regex는 matcher가 매치할 정규표현식입니다.

replaceValue는 매치된 경우 교체할 값입니다.

group은 정규표현식에서 캡쳐된 그룹을 연쇄적으로 처리할 category가 담긴 배열입니다.


translate(targetText = "The rebounding bolt of cold hits manticore.", language = "korean", startCategory = "message") 함수의 결과는 다음과 같습니다.


{

    "target": "The rebounding bolt of cold hits manticore.",

    "translation": "반동하는 냉기 화살이 만티코어를 때렸다.",

    "translations": [

        {

            "target": "The rebounding bolt of cold hits manticore.",

            "translation": "반동하는 냉기 화살이 만티코어를 때렸다.",

            "translations": [

                {

                    "target": "bolt of cold",

                    "translation": "냉기 화살",

                    "category": "magic",

                    "status": "translated"

                },

                {

                    "target": "manticore",

                    "translation": "만티코어",

                    "category": "target",

                    "status": "translated"

                }

            ],

            "category": "hit_messages",

            "status": "translated"

    ],

    "category": "message",

    "status": "translated"

}


1. translate 함수는 startCategory에 속한 matcher들을 모두 순회하면서 regex가 match되는 matcher를 찾습니다. 그리고 replaceValue로 현재 단계의 문자열을 교체합니다.
2. 매치되는 경우 group에 캡처된 그룹을 넘기고 group Category에 속한 matcher들을 모두 순회되면서 regex가 match되는 matcher를 찾습니다. 그리고 replaceValue로 현재 단계의 문자열을 교체합니다.
3. 그룹이 없는 케이스 즉, 원자적 교체가 일어날 때 까지 (2)를 반복합니다. 교체가 이루어지고 상위 단계로 계속 올라가게 되면 최종적으로는 계층적으로 번역이 완료된 텍스트를 얻게 됩니다.

만약 원자적 교체가 일어나지 않는 케이스의 경우 즉, matcher가 정의되지 않은 경우에는 다음과 같이 미번역 부분을 획득할 수 있게 되며, 이 미번역 데이터는 따로 수집되어 번역 목록에 올라가게 됩니다.
{
    "target": "The rebounding bolt of cold hits quokka.",
    "translation": "반동하는 냉기 화살이 quokka를 때렸다.",
    "translations": [
        {
            "target": "The rebounding bolt of cold hits quokka.",
            "translation": "반동하는 냉기 화살이 quokka를 때렸다.",
            "translations": [
                {
                    "target": "bolt of cold",
                    "translation": "냉기 화살",
                    "category": "magic",
                    "status": "translated"
                },
                {
                    "target": "quokka",
                    "status": "untranslated"
                }
            ],
            "category": "hit_messages",
            "status": "translated"
        }
    ],
    "category": "message",
    "status": "translated"
}

replaceValue에는 특별한 후처리 문법을 지원합니다.
'반동하는 {$1:이} {$2:를} 때렸다.'에서 '{}'는 후처리 함수 문법입니다. 이것을 정확히 무엇으로 지칭할지는 아직 생각하지 못했지만, 다음과 같이 작동합니다.
{param1,param2:funcName}과 같은 형식으로 사용하면, funcName(param1, param2)가 호출됩니다.
이를 통해 조사를 쉽게 처리할 수 있습니다. 예를들어 '이' 또는 '가' 함수를 정의하고 함수 로직으로 $1의 마지막 한글 글자의 종성을 검사한 뒤, 상황에 알맞는 '이'나 '가'를 반환하게 하면 상황에 맞는 조사로 해당 부분이 교체될 것입니다.

위와 같이 matcher JSON을 직접 기술하는 방식은 기능검증에는 유효하지만, 일반 번역자들이 사용하기에는 다소 복잡합니다. 따라서 번역을 위한 별도의 프로그램이 제작되어야 할 것입니다. 해당 프로그램은 다음과 같은 기능을 하여야 합니다.

1. wtrec들을 취합하여 번역해야 할 단위 텍스트들을 파싱하여 추출합니다. 여기서 단위 텍스트라 하면, wtrec에서 순수하게 번역이 이루어져야 하는 영어 문장입니다.
2. 단위 텍스트를 전부 translate 합니다. 이 함수의 결과를 통해 단위 텍스트는 full-translated인지, partial-translated인지, untranslated인지 상태가 결정됩니다.
3. (2)에서 partial-translated된 케이스의 group 처리 결과 중 untranslated된 텍스트는 따로 취합하여 저장합니다.
4. 단위 텍스트와 그 번역 상태, (3)의 추출된 group untranslated 결과를 적절하게 사용자에게 표시합니다. 사용자에게는 이것들을 보며 현재 번역 현황과 matcher를 정의할 수 있는 기능이 제공됩니다.
5. 사용자가 추가한 matcher가 제대로 작동하는지 확인하기 위해 (1-3)을 트리거할 수 있는 별도의 기능이 제공되어야 합니다.

실시간성에 대하여
계층적 번역이 이루어지는 만큼, startCategory의 matcher 수가 적절하다면 보장될 것으로 보입니다. 일부 장문 텍스트의 경우 해시를 이용하여 우선 매칭를 하는 것이 효율적일 수 있습니다.

DL판 적용에 대하여
우선 웹타일 빌드에서 온전히 작동되는 번역 모듈 빌드를 얻게 된다면, C++의 정규식 엔진을 통해 번역 모듈의 함수들을 재작성하여 포팅할 수 있을 것입니다. 이를 통해 번역이 이루어질 수 있을 것으로 보입니다. 다만 이 작업에는 아직 관심이 없습니다.

---
- 나중에 GPT 돌려서 영어로 만든 다음에, 아예 다국어화를 지원하는 모듈의 설명서를 만들 것이라 일부로 이렇게 딱딱하게 적음

- 여기까지 구체화 시키는데 4년 걸렸음, 지원하는데 관심이 있다면 CNC 서버 디스코드의 translation 채널에서 연락주면 될 듯

추천 비추천

8

고정닉 5

0

댓글 영역

전체 댓글 0
등록순정렬 기준선택
본문 보기

하단 갤러리 리스트 영역

왼쪽 컨텐츠 영역

갤러리 리스트 영역

갤러리 리스트
번호 제목 글쓴이 작성일 조회 추천
설문 소속 연예인 논란에 잘 대응하지 못하는 것 같은 소속사는? 운영자 25/04/21 - -
AD 작혼X페스나 헤븐즈필 콜라보 개시! 운영자 25/04/22 - -
공지 로그라이크 갤러리 이용 안내 [56] 운영자 16.04.08 88999 24
494661 드라코 칭호작하다가 현타오네 [1] 로갤러(222.235) 04:13 39 0
494660 ㄷㅈ)셰싶 경험치 보통 어케줌? [3] ㅇㅇ갤로그로 이동합니다. 04:00 33 0
494659 카타클 원래 불난곳에서 좀 떨어지면 화재 진행이 멈춤? [3] ㅇㅇ갤로그로 이동합니다. 03:17 27 0
(초안, 기술 문서) 돌죽 한국어화 - 1 [9] ASCIIPhilia갤로그로 이동합니다. 01:59 106 8
494655 ㅇㄹ)연주가 연주 스킬 말고 연주장비마다 다른가봐... [5] 익스비갤로그로 이동합니다. 01:47 57 0
494654 ㅌㅈ) 아 ㅁㅊ 인간 하이어로 기사 키우면 안되는구나 [7] Tree_Leaf갤로그로 이동합니다. 01:37 46 0
494653 돌죽 다깼다 [4] 트로그갤로그로 이동합니다. 01:27 65 0
494650 지금 양유 좋은거같음 [3] ㅇㅇ갤로그로 이동합니다. 00:01 59 0
494649 ㅋㅌㅋ 함정은 진짜 아쉽긴함 [5] 머방이갤로그로 이동합니다. 04.21 69 0
494647 ㄷㅈ)풍뎅이폼 태양 질문점 [3] ㅇㅇ갤로그로 이동합니다. 04.21 52 0
494646 ㅇㄹ) 이거 방어구나 무기도 분해하면 레시피 뜸? [3] ㅇㅇ(121.143) 04.21 69 0
494645 ㅋㅌㅋㅂㅂ) ㅋㅋㅋ 함정 이거 웃기는새끼네 [3] 점화자갤로그로 이동합니다. 04.21 57 1
494644 동방죽 스팀에 나오나봄 [2] ㅇㅇ갤로그로 이동합니다. 04.21 118 0
494643 ㄷㅈ) 문어 스핑크스는 특별한 게 없네 [1] ㅇㅇ갤로그로 이동합니다. 04.21 58 1
494642 ㅋㅌㅋㅂㅂ) 함정설치 올리기 까다롭지 않냐 [6] 점화자갤로그로 이동합니다. 04.21 54 0
494641 ㅂㅂ)애프터쇼크 로봇 분해모드가 분명로갤산으로있었는데 [10] seeu갤로그로 이동합니다. 04.21 79 0
494639 톨죽이 정확이 어떤 게임임? [8] Tree_Leaf갤로그로 이동합니다. 04.21 115 1
494638 ㅌㅈ 혹시 인세인? 이란게 뭔가요?? [3] 아사키렌(113.30) 04.21 56 0
494637 ㄷㅈ)몇년만에 다시하는데 뒤지게많이바꼈넹 [4] ㅇㅇ(61.41) 04.21 107 0
494636 ㄷㅈ)'당신의 양손은 한 쌍의 불경한 힘을 지닌 마검으로 변했다.' [11] ㅇㅇ(58.77) 04.21 135 0
494635 돌죽 창은 민첩무기로 해주면 안되는걸까 [3] 자연선혼갤로그로 이동합니다. 04.21 129 0
494633 요새 탈콥에 빠져서 콰지모프를 안하게되네 [2] Khelerd갤로그로 이동합니다. 04.21 88 0
494632 모바일 돌죽은 업데이트 안되는건가 [3] 로갤러(61.105) 04.21 64 1
494630 봉 괜찮아요?? [6] Koakuma갤로그로 이동합니다. 04.21 97 0
494629 ㅌㅈ 어뎁트 버그 패시브만 신경쓰면 되나? [1] 로갤러(221.144) 04.21 47 0
494628 전머협피셜 돌죽 복잡한 몹 1위 "Onichan-incarcerater" [3] ㅇㅇ갤로그로 이동합니다. 04.21 148 1
494627 카타클리즘 밝은밤) 대형마트(mall) 근처 렉 [22] Mistress갤로그로 이동합니다. 04.21 111 0
494626 레전드 놀림코드 발생 [4] ㅇㅇ갤로그로 이동합니다. 04.21 125 1
494625 막상 한입해보니까 잼네 [6] ㅇㅇ(183.96) 04.21 189 0
494624 모든것을 간단하게해줄 단 하나뿐인 열쇠 [2] ㅇㅇ(211.234) 04.21 99 0
494623 돌죽)리워크하는건지 좋지만 존나 복잡해질까봐 걱정이네 [4] 와그너스갤로그로 이동합니다. 04.21 157 2
494621 이번에 탈리스만 패치때 칼폼은 뭐 없었음? [5] ㅇㅇ(211.36) 04.21 88 0
494620 ㄷㅈ)석좆폼은 저항 감소 변이에 면역이네? [3] ㅇㅇ(223.39) 04.21 109 1
494619 뱀파이어 블러드프린스 이새키 공략법이 머임?? [2] ㅇㅇ(121.176) 04.21 93 1
494618 ㅋㅌㅋ 보물지도하니 석화된 안구 사원 생각나네 [4] 머방이갤로그로 이동합니다. 04.21 48 0
494617 판데리워크 글 원문봤는데 [4] 로갤러(222.235) 04.21 117 2
494616 ㄷㅈ) 딥참피 올룬클 [3] ㅇㅇ(106.255) 04.21 69 2
494614 ㄷㅈ) 그저 유일신.. [2] 로갤러(183.97) 04.21 152 2
494613 돌죽 판데모니엄이랑 지옥도 리워크 테스팅중이라는듯 [22] ㅁㄴㅇㄹ(211.180) 04.21 440 13
494612 바람에 날려버린~ 허무한 맹세였나~ ㅇㅇ(39.7) 04.21 43 0
494611 카타클이 근데 그렇게 폰 조작 어려움? [3] ㅇㅇ(106.102) 04.21 61 0
494609 ㄷㅈ)좀쟝 진짜 츤데레네 [1] ㅇㅇ갤로그로 이동합니다. 04.21 85 0
494608 ㄷㅈ)핸드캐논 뮬 이거 좋음? [1] ㅇㅇ갤로그로 이동합니다. 04.21 101 0
494607 ㅌㅈ) 마슬 초월무기 레이저락 좋을거같은데 [4] ㅇㅇ(112.187) 04.21 83 0
494605 ㄷㅈ) 폴터 인챈터 지이바로 올룬 25000턴대 성공 [12] 로갤러(61.74) 04.21 243 7
494604 ㅇㄹ 개척감사관 필요없다고하면 로이텔 안나오고 빚퀘스트 스킵됨? [1] 로갤러(182.220) 04.21 130 0
494603 ㄷㅈ) 댐네이션 쏘는 알바레스트 머임? [4] ㅇㅇ갤로그로 이동합니다. 04.21 120 0
494601 ㄷㅈ]골든 용폼 [4] 트로그갤로그로 이동합니다. 04.21 128 4
494600 선리스씨 한패 버그 제보 받습니다. [1] dd(223.38) 04.20 116 12
뉴스 에일리♥최시훈 결혼, 신혼여행은 두바이와 몰디브 디시트렌드 04.21
갤러리 내부 검색
제목+내용게시물 정렬 옵션

오른쪽 컨텐츠 영역

실시간 베스트

1/8

뉴스

디시미디어

디시이슈

1/2