유니티 게임 한글화 시 한국어 조사 처리

2025. 8. 27. 23:59·한글패치 관련 짧은 글들

csjosa 라이브러리를 활용해서 한국어 조사 처리를 해보도록 하겠습니다.
원본 텍스트엔 "(은)는" 형태로 조사가 기입되어 있어야 합니다.
 
 

0. 관련 툴 / 링크

csjosa 라이브러리(제작자 myevan): https://github.com/myevan/csjosa

GitHub - myevan/csjosa: c# 한글 조사 처리

c# 한글 조사 처리. Contribute to myevan/csjosa development by creating an account on GitHub.

github.com

 
DnSpyEX: https://github.com/dnSpyEx/dnSpy

GitHub - dnSpyEx/dnSpy: Unofficial revival of the well known .NET debugger and assembly editor, dnSpy

Unofficial revival of the well known .NET debugger and assembly editor, dnSpy - dnSpyEx/dnSpy

github.com

 

0. 테스트용 상황

UnityEngine.UI.Text, TextMeshPro
2가지 케이스로 진행해보겠습니다.
 
 
 

1. UnityEngine.UI.Text 파트

Managed 폴더의 UnityEngine.UI.dll 파일을 Dnspy에 로드한 후,
UnityEngine.UI 네임스페이스의 Text 클래스를 우클릭하여, Edit Class (C#) 버튼을 누릅니다.
 
일단, 제일 위 Using; 파트에 다음 코드를 추가합니다.

using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;

 
 
 

using을 추가하셨으면
마지막 코드 부분에서 엔터를 한칸 친 후, 아래 코드를 붙여넣습니다.
 

	private static class Csjosa
	{
		private class JosaPair
		{
			public JosaPair(string josa1, string josa2)
			{
				this.josa1 = josa1;
				this.josa2 = josa2;
			}
			public string josa1 { get; private set; }
			public string josa2 { get; private set; }
		}

		private static readonly System.Text.RegularExpressions.Regex _josaRegex = new System.Text.RegularExpressions.Regex(@"\(이\)가|\(와\)과|\(을\)를|\(은\)는|\(아\)야|\(이\)여|\(으\)로|\(이\)라");

		private static readonly System.Collections.Generic.Dictionary<string, JosaPair> _josaPatternPaird = new System.Collections.Generic.Dictionary<string, JosaPair>
		{
			{ "(이)가", new JosaPair("이", "가") },
			{ "(와)과", new JosaPair("과", "와") },
			{ "(을)를", new JosaPair("을", "를") },
			{ "(은)는", new JosaPair("은", "는") },
			{ "(아)야", new JosaPair("아", "야") },
			{ "(이)여", new JosaPair("이여", "여") },
			{ "(으)로", new JosaPair("으로", "로") },
			{ "(이)라", new JosaPair("이라", "라") },
		};

		public static string Process(string src)
		{
			if (string.IsNullOrEmpty(src))
			{
				return src;
			}
			var strBuilder = new System.Text.StringBuilder(src.Length);
			var josaMatches = _josaRegex.Matches(src);
			var lastHeadIndex = 0;
			foreach (System.Text.RegularExpressions.Match josaMatch in josaMatches)
			{
				var josaPair = _josaPatternPaird[josaMatch.Value];
				strBuilder.Append(src, lastHeadIndex, josaMatch.Index - lastHeadIndex);
				if (josaMatch.Index > 0)
				{
					var prevChar = src[josaMatch.Index - 1];
					if ((HasJong(prevChar) && josaMatch.Value != "(으)로") ||
						(HasJongExceptRieul(prevChar) && josaMatch.Value == "(으)로"))
					{
						strBuilder.Append(josaPair.josa1);
					}
					else
					{
						strBuilder.Append(josaPair.josa2);
					}
				}
				else
				{
					strBuilder.Append(josaPair.josa1);
				}
				lastHeadIndex = josaMatch.Index + josaMatch.Length;
			}
			strBuilder.Append(src, lastHeadIndex, src.Length - lastHeadIndex);
			return strBuilder.ToString();
		}

		private static bool HasJong(char inChar)
		{
			if (inChar >= 0xAC00 && inChar <= 0xD7A3)
			{
				return (inChar - 0xAC00) % 28 > 0;
			}
			return false;
		}

		private static bool HasJongExceptRieul(char inChar)
		{
			if (inChar >= 0xAC00 && inChar <= 0xD7A3)
			{
				int jongCode = (inChar - 0xAC00) % 28;
				return jongCode != 8 && jongCode != 0;
			}
			return false;
		}
	}

 
 
 

그 다음, "public virtual string text"를 검색하여, set 접근자를 수정합니다.
아래와 같이 수정하시면 됩니다.
 

	set
	{
		string processedValue = Csjosa.Process(value);

		if (!string.IsNullOrEmpty(processedValue))
		{
			if (this.m_Text != processedValue)
			{
				this.m_Text = processedValue;
				this.SetVerticesDirty();
				this.SetLayoutDirty();
			}
			return;
		}
		if (string.IsNullOrEmpty(this.m_Text))
		{
			return;
		}
		this.m_Text = "";
		this.SetVerticesDirty();
	}

 
 
 

마찬가지로 "protected override void OnEnable()"도 검색하셔서 아래와 같이 수정합니다.
 

protected override void OnEnable()
{
	base.OnEnable();

	if (!string.IsNullOrEmpty(this.m_Text))
	{
		this.m_Text = Csjosa.Process(this.m_Text);
	}

	this.cachedTextGenerator.Invalidate();
	FontUpdateTracker.TrackText(this);
}

 
이후 컴파일을 누르신 다음, File - Save All을 누르신 다음 게임을 실행해보시면
 

 
일단 잘 뜹니다.
 
 
 

2. TextMeshPro 파트

 

 
 
Unity.TextMeshPro.dll을 dnspy에 로드시키신 후,
TMPro - TMP_Text를 Edit Class 하신 다음, 최상단에 아래 코드를 추가시키시고,
* using Debug = UnityEngine.Debug;를 추가해주셔야 합니다.

using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using Debug = UnityEngine.Debug;

 
 
 

커서 부분에 아래 코드를 추가합니다
. 

	private static class Csjosa
	{
		private class JosaPair
		{
			public JosaPair(string josa1, string josa2)
			{
				this.josa1 = josa1;
				this.josa2 = josa2;
			}
			public string josa1 { get; private set; }
			public string josa2 { get; private set; }
		}

		private static readonly System.Text.RegularExpressions.Regex _josaRegex = new System.Text.RegularExpressions.Regex(@"\(이\)가|\(와\)과|\(을\)를|\(은\)는|\(아\)야|\(이\)여|\(으\)로|\(이\)라");

		private static readonly System.Collections.Generic.Dictionary<string, JosaPair> _josaPatternPaird = new System.Collections.Generic.Dictionary<string, JosaPair>
		{
			{ "(이)가", new JosaPair("이", "가") },
			{ "(와)과", new JosaPair("과", "와") },
			{ "(을)를", new JosaPair("을", "를") },
			{ "(은)는", new JosaPair("은", "는") },
			{ "(아)야", new JosaPair("아", "야") },
			{ "(이)여", new JosaPair("이여", "여") },
			{ "(으)로", new JosaPair("으로", "로") },
			{ "(이)라", new JosaPair("이라", "라") },
		};

		public static string Process(string src)
		{
			if (string.IsNullOrEmpty(src))
			{
				return src;
			}
			var strBuilder = new System.Text.StringBuilder(src.Length);
			var josaMatches = _josaRegex.Matches(src);
			var lastHeadIndex = 0;
			foreach (System.Text.RegularExpressions.Match josaMatch in josaMatches)
			{
				var josaPair = _josaPatternPaird[josaMatch.Value];
				strBuilder.Append(src, lastHeadIndex, josaMatch.Index - lastHeadIndex);
				if (josaMatch.Index > 0)
				{
					var prevChar = src[josaMatch.Index - 1];
					if ((HasJong(prevChar) && josaMatch.Value != "(으)로") ||
						(HasJongExceptRieul(prevChar) && josaMatch.Value == "(으)로"))
					{
						strBuilder.Append(josaPair.josa1);
					}
					else
					{
						strBuilder.Append(josaPair.josa2);
					}
				}
				else
				{
					strBuilder.Append(josaPair.josa1);
				}
				lastHeadIndex = josaMatch.Index + josaMatch.Length;
			}
			strBuilder.Append(src, lastHeadIndex, src.Length - lastHeadIndex);
			return strBuilder.ToString();
		}

		private static bool HasJong(char inChar)
		{
			if (inChar >= 0xAC00 && inChar <= 0xD7A3)
			{
				return (inChar - 0xAC00) % 28 > 0;
			}
			return false;
		}

		private static bool HasJongExceptRieul(char inChar)
		{
			if (inChar >= 0xAC00 && inChar <= 0xD7A3)
			{
				int jongCode = (inChar - 0xAC00) % 28;
				return jongCode != 8 && jongCode != 0;
			}
			return false;
		}
	}

 
 
 
public virtual string text의 set 접근자 부분을 다음과 같이 수정합니다.

			set
			{
				if (!this.m_IsTextBackingStringDirty && this.m_text != null && value != null && this.m_text.Length == value.Length && this.m_text == value)
				{
					return;
				}
				this.m_IsTextBackingStringDirty = false;
				this.m_text = Csjosa.Process(value);
				this.m_inputSource = TMP_Text.TextInputSources.TextString;
				this.m_havePropertiesChanged = true;
				this.SetVerticesDirty();
				this.SetLayoutDirty();
			}

 
 
public void SetText(string sourceText, bool syncTextInputBox = true) 메서드의 내용도 다음과 같이 수정합니다.

    string sourceText = Csjosa.Process(sourceText);
    int num = ((sourceText == null) ? 0 : sourceText.Length);
    this.PopulateTextBackingArray(sourceText, 0, num);
    this.m_text = sourceText;
    this.m_inputSource = TMP_Text.TextInputSources.TextString;
    this.PopulateTextProcessingArray();
    this.m_havePropertiesChanged = true;
    this.SetVerticesDirty();
    this.SetLayoutDirty();

 
 

3. dnspy 오류 처리

이런 에러가 뜬다면 DnSpy를 재시작하세요.

저작자표시 비영리 (새창열림)

'한글패치 관련 짧은 글들' 카테고리의 다른 글

유니티 SDF폰트 이식 관련 파이썬 스크립트  (0) 2025.09.02
UnityPy 텍스쳐 삽입 시 "params must be an instance of BC7CompressBlockParams" 오류 해결  (0) 2025.03.31
global-metadata.dat이 없는 경우의 덤프법 (frida 이용)  (0) 2025.03.23
UnityPy를 이용한 유니티 게임 MonoBehaviour 특정 텍스트 필드 추출/삽입  (0) 2025.03.04
IoStore를 사용하며 sig우회가 안되는 언리얼 게임 모드 로딩 방지 우회하기  (6) 2025.01.20
'한글패치 관련 짧은 글들' 카테고리의 다른 글
  • 유니티 SDF폰트 이식 관련 파이썬 스크립트
  • UnityPy 텍스쳐 삽입 시 "params must be an instance of BC7CompressBlockParams" 오류 해결
  • global-metadata.dat이 없는 경우의 덤프법 (frida 이용)
  • UnityPy를 이용한 유니티 게임 MonoBehaviour 특정 텍스트 필드 추출/삽입
Snowyegret
Snowyegret
기록용 블로그입니다.
  • Snowyegret
    Snowyegret
    Snowyegret
  • 공지사항

    • 문의: 디스코드 snowyegret12
    • 연락처(디스코드) 계정 변경 안내
    • 블로그 운영 재개 안내
  • 링크

    • 블로그 메인
    • 방명록/문의사항
    • Github Repositories
    • Github Gists
    • 분류 전체보기 (87) N
      • 늑대와 향신료 VR2 한글패치 (1)
      • 장난을 잘 치는 타카기양 VR 1학기 한글패치 (1)
      • Shephy 카드 설명 한국어 텍스쳐 모드 (1)
      • 한글패치 관련 짧은 글들 (37)
      • 한글패치 작업 내역 (11)
      • 한글화 분석 (작업X) (5)
      • python snippets (4)
      • 게임 (2)
      • 간단프로그램 (2)
      • IT (18) N
      • TODO (2)
  • 인기 글

  • 최근 글

  • 최근 댓글

  • hELLO· Designed By정상우.v4.10.5
Snowyegret
유니티 게임 한글화 시 한국어 조사 처리
상단으로

티스토리툴바