우리가 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 |