본문으로 바로가기

언리얼엔진의 FText는 조사를 어떻게 판별할까?

category UnrealEngine/Impl 2025. 7. 12. 23:42

우리가 FText를 통해 Localization을 진행할 때

한글에서는 적절한 조사를 붙이기 위해 다음과 같이 처리한 것이 있을 것이다.

{0}|hpp(은,는)

여기서 정의는
{0}|hpp(받침이 들어올 때 쓰일 조사, 받침이 들어오지 않을 때 쓰일 조사)

 

그런데 언리얼엔진은 {0}에 어떤 텍스트가 들어올지 어떻게 알고 은, 는을 따로 나누어서 붙여주는 것일까?

이는 실제로 포매팅이 진행될 FTextFormatData::Format_NoLock 까지 들어가야 한다.

(여기서 Lock이 있는 이유는, 포매팅에 대해 Multithread도 지원할 수 있기 때문이다.)

 

FString FTextFormatData::Format_NoLock(const FPrivateTextFormatArguments& InFormatArgs)
{
	// Impl
	ArgumentModifierToken->TextFormatArgumentModifier->Evaluate(*PossibleArgumentValue, InFormatArgs, ResultString);
}

 

여기서 TextFormatArgumentModifier는 해당 텍스트가 한글이라면

FTextFormatArgumentModifier_HangulPostPositions가 들어온다.

void FTextFormatArgumentModifier_HangulPostPositions::Evaluate(const FFormatArgumentValue& InValue, const FPrivateTextFormatArguments& InFormatArgs, FString& OutResult) const

 

이 부분의 Implementation을 확인하면 된다.

 

코드를 하나하나 봐보자

 

if ((LastArgChar >= 0xAC00 && LastArgChar <= 0xD7A3) || (LastArgChar >= TEXT('0') && LastArgChar <= TEXT('9')))

 

LastArgChar가 [0xAC00, 0xD7A3] 안에 있거나 [0, 9] 에 있는지 확인한다.

여기서 0xAC00 의 유니코드는 ''

0xD7A3의 유니코드는 '' 이다. 

 

 

마지막 문자가 한글이거나 숫자인지를 확인한다.

bool bIsConsonant = ((LastArgChar - 0xAC00) % 28 != 0) || LastArgChar == TEXT('0') || LastArgChar == TEXT('1') || LastArgChar == TEXT('3') || LastArgChar == TEXT('6') || LastArgChar == TEXT('7') || LastArgChar == TEXT('8');

숫자부터

LastArgChar가 0, 1, 3, 6, 7, 8 이라면 받침이 있는 것이다.

예를들어

'1은'  = '일은' 이고

'2는' = "이는" 이므로 0, 1, 3, 6, 7, 8 은 받침이 있는 조사로 처리되어야 할 것이다.

 

또한

(LastArgChar - 0xAC00) % 28 != 0 이라는 코드가 있다.

이는 유니코드의 받침이 어떻게 처리되는지 알아야 한다.

 

우리가 예를들어 

길 이라는 텍스트를 쓴다고 해보자 이 길이라는 텍스트는 

AE38 이다.

 

한글 음정의 유니코드는 다음과 같이 정해진다.

초성 * 21 * 28 + 중성 * 28 + 종성

 

여기서 초성 | 중성 | 종성은 우리의 자음 모음 순서이다.

길은 ㄱ + ㅣ + ㄹ 이며

인덱스는 0, 20, 8 이다.

 

한글은 19개의 자음과 10개의 모음인데

왜 ㅣ 은 인덱스가 20 이고

ㄹ은 인덱스가 8일까?

 

이건 초성, 중성, 종성이 어떻게 이루어져 있는지를 봐야 하는데, 표를 보면 이해가 쉬울 것이라고 생각한다 

 

초성 인덱스

0 기본 자음
1 된소리(쌍기역)
2  
3  
4 된소리(쌍디귿)
5  
6  
7  
8 된소리(쌍비읍)
9  
10 된소리(쌍시옷)
11  
12  
13 된소리(쌍지읒)
14  
15  
16  
17  
18  

 

중성 인덱스

0 기본 단모음
1  
2  
3  
4  
5  
6  
7  
8 단모음 (복합 조합 가능한 모음)
9 ㅗ + ㅏ
10 ㅗ + ㅐ
11 ㅗ + ㅣ
12  
13  
14 ㅜ + ㅓ
15 ㅜ + ㅔ
16 ㅜ + ㅣ
17  
18  
19 ㅡ + ㅣ
20  

 

종성 인덱스

0 (없음) 받침 없음
1 기본 자음
2 쌍기역
3 ㄱ + ㅅ (겹받침)
4  
5 ㄴ + ㅈ
6 ㄴ + ㅎ
7  
8  
9 ㄹ + ㄱ
10 ㄹ + ㅁ
11 ㄹ + ㅂ
12 ㄹ + ㅅ
13 ㄹ + ㅌ
14 ㄹ + ㅍ
15 ㄹ + ㅎ
16  
17  
18 ㅂ + ㅅ
19  
20 쌍시옷
21  
22  
23  
24  
25  
26  
27  

 

종성은 없을수도 있고,

초성과 다른 복합자음(겹받침)이 받침으로 들어갈 수 있기 때문이다.

 

위 표에 맞춰서 '길' 이라는 텍스트를 다시 계산하면

길은 ㄱ + ㅣ + ㄹ 이며

인덱스는 0, 20, 8 이다.

 

이 인덱스를 위의 식에 계산해본다면 길은 568 이라는 값이 나오고

한글 유니코드의 시작인 0xAC00 에 568를 더하면 0xAE38 이 나온다.

 

그럼 다시 위의 FText 코드로 돌아가본다면

(LastArgChar - 0xAC00) % 28 != 0

이는 결국

LastArgChar가 종성 28개로 Modular 연산을 했을때 0이 아니라면 받침이 있다는 뜻이다. 라는것으로 확인할 수 있다.

 

한글의 뒷 글자가 받침이 있다면 이것은 '받침이 있을 때 들어오는 조사' 로 포매팅 되어야 한다는 것이다.

 

const bool bIsRieul = ((LastArgChar - 0xAC00) % 28 == 8) || LastArgChar == TEXT('1') || LastArgChar == TEXT('7') || LastArgChar == TEXT('8');
if (bIsRieul)
{
	bIsConsonant = false;
}

 

bIsRieul은 해당 문자가 ㄹ 받침을 쓰는지 확인한다

'ㄹ' 은 인덱스로 8 이므로 LastArgChar - 0xAC00 % 28 (종성갯수) 했을 때 8이 나오면 이는 ㄹ이다.

ㄹ받침인 경우에는 '받침이 없는 조사'가 들어와야 한다는 것이다.

 

if (bIsConsonant)
{
	OutResult.AppendChars(ConsonantSuffix.StringPtr, ConsonantSuffix.StringLen);
}
else
{
	OutResult.AppendChars(VowelSuffix.StringPtr, VowelSuffix.StringLen);
}

 

받침이 있다는 것은 곧, 자음이 이곳에 들어왔다는 것이고 그래서 bIsConsonant를 사용한다.

이게 true 라면 '은' 이 붙을 것이고

false라면 '는' 이 붙을 것이다.

 

'UnrealEngine > Impl' 카테고리의 다른 글

Unreal Engine의 FText 내부까지 알아보기  (0) 2024.07.21
NewObject와 SpawnActor  (0) 2023.10.03
SpawnActor와 SpawnActorDeferred  (0) 2023.10.02
UnrealEngine의 UObject 와 Components  (2) 2023.09.30
Hard References와 Soft References  (0) 2023.09.28