로컬라이징의 이해

# 학습 과정 소개

크리에이터가 제작한 월드를 다양한 지역의 유저들이 즐길 수 있도록 하려면 로컬라이징 과정은 필수적입니다. `LocalizationService`는 클라이언트의 언어를 기반으로 텍스트나 어셋 등을 해당 언어에 맞게 제공할 수 있도록 도와줍니다. 이번 과정에서는 로컬라이징의 개념을 살펴보고 `LocalizationService`를 사용해 텍스트를 번역하는 방법을 알아봅시다.

##### 참고 가이드

[포맷에 맞게 로컬라이징하기](docs/?postId=968) [자동 번역](docs/?postId=1072) [LocalDataSet](docs?postId=1120)

##### 참고 API Reference

[LocalizationService](/apiReference/Services/LocalizationService)

# 로컬라이징이란?

좁은 의미의 로컬라이징은 기본적으로 월드에서 유저가 보는 단어나 문장을 번역하는 것입니다. 예를 들어 크리에이터가 지원 언어를 '한국어'로만 설정한 월드가 있습니다. 이 월드에 클라이언트 언어를 영어로 설정한 유저가 접속했을 때 단어 사과를 Apple로 번역하는 것입니다.

로컬라이징 의미를 넓게 보면 월드의 언어와 여러 요소를 각 언어 및 문화권에 맞게 아울러 지원하는 것입니다. 예를 들어 날짜 표기법이 다릅니다. 한국의 날짜 표기법은 연/월/일 순입니다. 2023년 1월 31일은 "2023/01/31"과 같은 방식으로 씁니다. 반면에 미국, 캐나다 등에서는 보통 월/일/년 (01/31/2023)으로 표현합니다. 또 다른 문화권에서는 일/월/년 (31/01/2023)으로 표현하기도 합니다. 다른 예시로 문화권 별로 다른 숫자 표기 방식이 다릅니다. 1234567.89 숫자를 한국, 미국, 캐나다 등에서는 보통 천 단위 구분 기호로 쉼표(,)를 사용하고 소수점으로는 마침표(.)를 사용해 1,234,567.89로 표기합니다. 하지만 프랑스, 독일, 오스트리아 등에서는 천 단위 구분 기호로 마침표(.)를 사용하고 소수점으로 쉼표(,)를 사용해 1.234.567,89로 표기합니다. 또 다른 문화권에서는 1 234 567.89과 같이 천 단위 구분 기호를 따로 쓰지 않고 그냥 공백으로 표현하기도 합니다.

따라서 여러 언어 문화권 유저의 편의를 고려해 날짜, 숫자를 문화권에 맞는 형식으로 표현할 필요가 있습니다. 로컬라이징은 단순한 번역이 아닌 각 문화권에서 접속하는 유저가 불편 없이 콘텐츠를 이용할 수 있도록 조정하는 과정 전체를 통칭합니다.

## 메이플스토리 월드의 로컬라이징

메이플스토리 월드는 장기적으로 글로벌 원빌드를 지향합니다. 그렇기에 하나의 서버에 여러 지역에서 접속한 클라이언트가 함께 연결될 수도 있습니다. 때문에 메이플스토리 월드에서는 하나의 텍스트를 동시에 여러 언어로 번역할 수 있도록 지원하고 있습니다. [png](167479872265151ac9e8a10ee49638f070ced7e26e7b1)

다만 서버에서는 로컬라이징된 메시지 자체를 직접 발송하는 것이 아니라 로컬라이징 키(Localizing Key)를 보내야 합니다. 로컬라이징 키로 통신하며 클라이언트 언어 설정 별로 다르게 출력하면 됩니다. 메이플스토리 월드에서 실제 로컬라이징을 어떤 방식으로 할 수 있는지 예제와 함께 살펴보도록 하겠습니다.

# 텍스트 번역하기

클라이언트 언어 설정에 따라 텍스트를 번역하려면 번역 테이블을 생성해야 합니다..

#### LocaleDataSet 만들기

LocaleDataSet을 활용해 간단한 방법으로 텍스트를 번역해 봅시다.

  1. UI 에디터에서 텍스트 엔티티를 추가합니다. [png](1686728912583c7cafe2f6fa244b49e51195f776c6ed2) 추가한 텍스트 엔티티의 TextComponent에서 프로퍼티를 아래와 같이 수정합니다.

[{"컴포넌트":"TextComponent","프로퍼티":"FontSize","값":"50.0"},{"컴포넌트":"TextComponent","프로퍼티":"Text","값":"UI_Hello"},{"컴포넌트":"TextComponent","프로퍼티":"IsLocalizationKey","값":"True"}]

  2. LocaleDataSet을 생성하고 아래와 같이 입력합니다.

[{"index":"ID","0":"1"},{"index":"Key","0":"UI_Hello"},{"index":"Source","0":"Hello!"},{"index":"ko","0":"안녕하세요!"},{"index":"en","0":"Hello!"}]

  3. 클라이언트의 언어 설정이 한국어인 상태에서  [시작] 버튼을 누른 뒤 테스트해 봅시다. 안녕하세요!라고 출력되는 것을 확인할 수 있습니다. [png](1674808092988390b7fa2c4e648b28503ad01d9a744a5)

  4. 상단 메뉴에서 언어 설정을 한국어를 영어로 변경합니다. [png](1720601013220ffb2496a775d4a3190fe5ddbea8d7ad2)

  5.  [시작] 버튼을 누른 뒤 테스트해 봅시다. Hello!라고 출력됩니다.

[png](167480819908819eb8fb051a2483d878600f5f3fa548f)




## GetText 함수

TextComponent의 텍스트는 IsLocalizationKey로 간단하게 번역 여부를 설정할 수 있지만 그 외의 텍스트를 번역해야 하거나, 스크립트에서 번역을 제어할 때는 `GetText()` 함수를 활용합니다. `GetText()` 함수는 번역 테이블의 Key열에서 인수(argument)로 받은 키와 같은 값을 가진 행을 찾아 해당 행에서 클라이언트 언어 코드와 동일한 열의 텍스트 값을 반환합니다.

간단한 예제를 통해 클라이언트의 언어 설정별 텍스트 출력 방법을 알아봅시다. 맵에 2명의 NPC를 배치하고 언어 설정에 따라 적절한 이름을 출력해 보겠습니다.

  1. 맵에 NPC 2명을 배치합니다. 본 예제에서는 아래의 두 NPC를 배치했습니다. [png](167410631018585e61a9abb2544af96dbb18c261313f3)

     * npc-931
     * npc-845
  2. 각 NPC 엔티티의 이름을 아래와 같이 수정합니다.

[{"NPC":"[png](1674097464492fb3054921620425a80664d1380f3f695)","Entity Name":"Stan"},{"NPC":"[png](16740974755475f8d0c294b5a491f907eefb8acc7bad1)","Entity Name":"Rina"}]

  3. LocaleDataSet을 생성하고 아래와 같이 입력합니다.

[{"ID":"1","Key":"UI_TEXT_NPC_Stan","Source":"Stan","ko":"스탄","en":"Stan"},{"ID":"2","Key":"UI_TEXT_NPC_Rina","Source":"Rina","ko":"리나","en":"Rina"}]

  4. Workspace - MyDesk의 콘텍스트 메뉴에서 Create Scripts - Create Logic을 클릭하고 LocalizationTest라는 이름으로 새 로직을 추가합니다.

  5. LocalizationTest에 아래와 같이 프로퍼티를 추가합니다.
         
         @Component
         script CharacterReferences extends Component
         property Entity Stan = "/maps/map01/Stan"
         property Entity Rina = "/maps/map01/Rina"
         end
         



    
    
        
    6. `OnBeginPlay()` 함수를 추가하고 아래와 같이 작성합니다. 실행 공간은 **client only**로 설정합니다.
        ```lua
    @Component
    script LocalizedNPCNameInitializer extends Component
        @ExecSpace("ClientOnly")
        method void OnBeginPlay()
        self.Stan.NameTagComponent.Name = _LocalizationService:GetText("UI_TEXT_NPC_Stan")
        self.Rina.NameTagComponent.Name = _LocalizationService:GetText("UI_TEXT_NPC_Rina")
        end
    end
    

  7. 클라이언트의 언어 설정이 한국어인 상태에서  [시작] 버튼을 누른 뒤 테스트해 봅시다. NPC 이름이 아래와 같이 나오는 것을 확인할 수 있습니다. [png](16741065097102ce4bfb23e3c453aaae7dc1b04f14365)   
언어 설정을 영어로 바꾸면 NPC 이름이 아래와 같이 영어로 나오는 것을 확인할 수 있습니다. [png](1674106659362b89a441ef8b045ed811972bb1bdd84e7)



### Translator

LocalizationService에는 LocalTranslator 프로퍼티가 있습니다.

  * LocalTranslator 프로퍼티의 타입은 [Translator](/apiReference/Misc/Translator)입니다.
  * LocalTranslator는 LocaleId(string 타입 프로퍼티)를 갖고 있습니다.



다음 두 줄의 코드는 같은 값을 반환합니다.
    
    
    @Component
    script ServerLocalizationLoader extends Component
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
            _LocalizationService:GetText("UI_TEXT_NPC_Stan")                   -- 스탄
            _LocalizationService.LocalTranslator:GetText("UI_TEXT_NPC_Stan")     -- 스탄
        end
    end
    

LocalTranslator의 LocaleId는 현재 실행 중인 클라이언트의 언어 코드입니다. 예를 들어, 현재 실행 중인 클라이언트의 언어가 한국어라면 LocalTranslator의 LocaleId는 ko입니다. LocalizationService에서 별도로 생성한 Translator를 사용하지 않고 곧바로 `GetText()`등의 함수를 호출하면 LocalTranslator의 LocaleId를 기준으로 기능을 수행합니다. 만일 현재 클라이언트 언어 설정이 한국어인데 영어 번역을 가지고 오고 싶다면 다음과 같이 코드를 작성하면 됩니다.
    
    
    @Component
    script LocalizedTextLoader extends Component
        @ExecSpace("ServerOnly")
        method void OnBeginPlay()
            local enTranslator = _LocalizationService:GetTranslatorForLocale("en")
            enTranslator:GetText("UI_TEXT_NPC_Stan") -- Stan
        end
    end
    

# 리소스 로컬라이징

텍스트 뿐만 아니라 리소스도 필요에 따라 로컬라이징할 수 있습니다.

예제를 통해 살펴봅시다.

  1. UI 에디터에서 [  ] 이미지 버튼을 눌러 빈 이미지를 생성하고 이름을 SpriteTest로 변경합니다.

  2. 새 스크립트 컴포넌트 SpriteLocalization를 생성합니다. 그리고 위에 생성한 SpriteTest 이미지에 컴포넌트를 추가합니다.

  3. SpriteLocalization 스크립트를 열고 아래와 같이 프로퍼티를 추가합니다.
         
         @Component
         script ResourceReference extends Component
         property string ResourceKey = ""
         end
         



    
    
      
    4. `OnInitialize()` 함수를 추가하고 아래와 같이 작성합니다. 실행 공간은 **client only**로 설정합니다.
    
        ```lua
    @Component
    script SpriteLocalizer extends Component
        @ExecSpace("ClientOnly")
        method void OnInitialize()
        self.Entity.SpriteGUIRendererComponent.ImageRUID = _LocalizationService:GetText(self.ResourceKey)
        end
    end
    
    
    
    

  5. SpriteTest의 프로퍼티 에디터에서 SpriteLocalization - ResourceKey에 RESOURCE_LOGO라고 입력합니다.

[png](1674184215431d6a44d479721419f9e694e289de686ce)

  6. LocaleDataSet을 열고 아래 내용을 추가합니다.

     * Key : RESOURCE_LOGO
     * Source : 3110502e753742d39794e042c2657ce5
     * ko : 7ae6a288ee884b75a44851c59b595789
     * en : 3110502e753742d39794e042c2657ce5   

  7.  [시작] 버튼을 누른 뒤 테스트해 봅시다. 언어 설정이 한국어일때는 한국어 로고 이미지가, 영어일 때는 영어 로고 이미지가 나옵니다.

[png](16741847984983e92b71d5e1f48d3b3469a98d7feb597)