[Unity-Localization] 유니티 현지화 구현 (다국어 번역)

sonsazang

·

2022. 7. 1. 15:17

서론

최근 회사에서 진행하게 될 프로젝트 중 새롭게 요청된 기능 중 하나인 현지화(Localization) 기능 - 다국어 번역 관련해서 구현하게 된 방향과 추후에 지식공유를 위한 정리 글입니다.

 

프로젝트 링크

https://github.com/SONSAZANG/UnityLocalizationTest

 

GitHub - SONSAZANG/UnityLocalizationTest: UnityLocalizationTest

UnityLocalizationTest. Contribute to SONSAZANG/UnityLocalizationTest development by creating an account on GitHub.

github.com

 

우선 유니티에서 현지화 기능을 사용할 때 진행할 수 있는 방향을 두 가지 정도로 확인했습니다.

 

1. CSV 파일을 활용한 현지화 구현 - 본 예제에서는 Google Sheets를 활용합니다.

2. Unity 공식 라이브러리인 Localization 활용한 현지화 구현


구현

1. CSV 파일을 활용한 현지화 구현

이번 예제에서는 Google Sheets를 활용합니다.

우선 구글 스프레드시트를 활용해서 아래와 같은 번역 테이블을 작성합니다.

우측에 작성한 주석을 참고하시면서 작성하시면 도움이 될 것 같습니다.

각 열에 각 나라의 언어를 작성하는 방식이며 추가적인 언어 설정이 필요할 경우 각 나라의 국가코드를 활용하면 가능합니다.

국가코드 위키

그다음 해당 문서를 유니티에서 불러와서 후처리 과정을 통해 진행하는 방식입니다.

디자인 패턴인 싱글톤 방식을 사용하여 스크립트를 구성한 후에 아래와 같은 코드를 작성합니다.

구글 스프레트시트를 tsv 형식으로 불러온 후 이차원 배열을 생성하여서 각 나라의 언어 항목으로 정렬하는 스크립트입니다.

// Singleton.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;

[System.Serializable]
public class Lang
{
    public string lang, langLocalize;
    public List<string> value = new List<string>();
}

public class Singleton : MonoBehaviour
{
    public static Singleton S;

    private void Awake()
    {
        if (S == null)
        {
            S = this;
            DontDestroyOnLoad(this);
        }

        else Destroy(this);

        InitLang();
    }

    private void Update()
    {
        if (Input.GetKey(KeyCode.Alpha1)) SceneManager.LoadScene(0);
        else if (Input.GetKey(KeyCode.Alpha2)) SceneManager.LoadScene(1);
    }

    const string langURL = "https://docs.google.com/spreadsheets/d/1aqRJDOChemD72n2JUB3KDcXuZipfYqdHi0P3uu37vTg/export?format=tsv";
    public event System.Action LocalizeChanged = () => { };
    public event System.Action LocalizeSettingChanged = () => { };

   

    public int curLangIndex;    
    public List<Lang> Langs;   


    
    void InitLang()
    {
        int langIndex = PlayerPrefs.GetInt("LangIndex", -1);
        int systemIndex = Langs.FindIndex(x => x.lang.ToLower() == Application.systemLanguage.ToString().ToLower());
        if (systemIndex == -1) systemIndex = 0;
        int index = langIndex == -1 ? systemIndex : langIndex;

        SetLangIndex(index); 
    }


    public void SetLangIndex(int index)
    {
        curLangIndex = index; 
        PlayerPrefs.SetInt("LangIndex", curLangIndex); 
        LocalizeChanged(); 
        LocalizeSettingChanged();
    }


    [ContextMenu("언어 가져오기")]  
    void GetLang()
    {
        StartCoroutine(GetLangCo());
    }

    IEnumerator GetLangCo()
    {
        UnityWebRequest www = UnityWebRequest.Get(langURL); 
        yield return www.SendWebRequest();  
        SetLangList(www.downloadHandler.text); 
    }

    void SetLangList(string tsv)
    {
        string[] row = tsv.Split('\n');
        int rowSize = row.Length;
        int columnSize = row[0].Split('\t').Length;
        string[,] Sentence = new string[rowSize, columnSize]; 

        for (int i = 0; i < rowSize; i++)
        {
            string[] column = row[i].Split('\t');
            for (int j = 0; j < columnSize; j++)
                Sentence[i, j] = column[j];
        }

        Langs = new List<Lang>();

        for (int i = 0; i < columnSize; i++)
        {
            Lang lang = new Lang();
            lang.lang = Sentence[0, i];
            lang.langLocalize = Sentence[1, i];

            for (int j = 2; j < rowSize; j++) lang.value.Add(Sentence[j, i]);
            Langs.Add(lang); 
        }
    }
}

그 후 각 나라를 불러와서 Unity - Dropdown에 적용하는 스크립트를 작성합니다.

dropdown 메뉴를 활용해서 언어를 변경하기 위한 행동입니다.

LocalizeSettingsChanged() 함수를 활용해 나라가 변경될 경우 각 언어 값들이 변경될 수 있도록 구성합니다.

// LocalizeSetting.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using static Singleton;

public class LocalizeSetting : MonoBehaviour
{
    Dropdown dropdown;

    private void Start()
    {
        dropdown = GetComponent<Dropdown>();
        if (dropdown.options.Count != S.Langs.Count)  
            SetLangOption();
        dropdown.onValueChanged.AddListener((d) => S.SetLangIndex(dropdown.value)); 

    }

    private void OnDestroy()
    {
        S.LocalizeSettingChanged -= LocalizeSettingChanged;
    }

    void SetLangOption()
    {
        List<Dropdown.OptionData> optionDatas = new List<Dropdown.OptionData>();

        for (int i = 0; i < S.Langs.Count; i++)
        {
            optionDatas.Add(new Dropdown.OptionData() { text = S.Langs[i].langLocalize });
        }

        dropdown.options = optionDatas;
    }

    void LocalizeSettingChanged()
    {
        dropdown.value = S.curLangIndex;
    }
}

그리고 현지화를 원하는 Text or Dropdown 요소에 위 함수에서 나라가 변경될 경우 string 값이 변경되게 하는 스크립트를 작성합니다.

// LocalizeScript.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using static Singleton;

public class LocalizeSrcript : MonoBehaviour
{
    public string textKey;      //0번째 열(영어 데이터)을 기준으로 key를 담을 문자열 선언 
    public string[] dropdownKey;    //만약 text가 아닌 드랍다운일 경우

    private void Start()
    {
        LocalizeChanged();
        S.LocalizeChanged += LocalizeChanged;
    }

    private void OnDestroy()
    {
        S.LocalizeChanged -= LocalizeChanged;
    }

    string Localize(string key) //어떤 문자열이 필요하진 key를 매개변수로 받는다 
    {
        int keyIndex = S.Langs[0].value.FindIndex(x => x.ToLower() == key.ToLower()); //기준이 되는 0번 기준으로 value 탐색 후 keyIndex에 문자열 선언
        return S.Langs[S.curLangIndex].value[keyIndex];      //현재 언어 인덱스, value의 key를 기준으로 문자열을 반환한다. 
    }

    void LocalizeChanged()
    {
        if (GetComponent<Text>() != null)
        {
            GetComponent<Text>().text = Localize(textKey);  // Localize의 반환 값으로 텍스트를 바꿔준다.
        }

        else if (GetComponent<Dropdown>() != null)
        {
            Dropdown dropdown = GetComponent<Dropdown>();
            dropdown.captionText.text = Localize(dropdownKey[dropdown.value]);

            for (int i = 0; i < dropdown.options.Count; i++)
            {
                dropdown.options[i].text = Localize(dropdownKey[i]);
            }
        }
    }
}

해당 스크립트를 작성 후 각 컴포넌트에 부착하여서 값의 키를 입력합니다.

Text 예시
Dropdown 예시

그 후 플레이할 경우 현지화 기능 확인이 가능할 것이다.


 

2. Unity 공식 라이브러리인 Localization 활용한 현지화 구현

우선 해당 라이브러리를 설치하기 위해 유니티 도큐먼트에 접속합니다. -> 링크

 

Installation guide | Localization | 1.0.5

Installation guide This package is currently visible in the Package Manager in 2021.2 and above. To install this package, follow the instructions in the Package Manager documentation. To install the package in a version before 2021.2, type in com.unity.loc

docs.unity3d.com

그리고 위에 사진에 표시되어 있는 " com.unity.localization " 부분을 유니티 에디터에서 해당 라이브러리를 설치합니다.

유니티 -> Window -> Package Manager -> [ + ] -> Add package from git URL... -> com.unity.localization 입력

라이브러리가 정상적으로 설치되면 Localization 라이브러리 확인이 가능할 것이다.

라이브러리 설치 완료 후 기본적인 현지화 테이블(Localization Table)을 생성합니다.

Window -> Asset Management -> Localization Tables -> Create -> 프로젝트에 Localization Settings.asset 저장

생성된 현지화 테이블에서 원하는 테이블 이름으로 Name을 변경 후 (필자는 Uitexts 사용) -> Locale Generator: 현지화 원하는 언어 선택 -> English, Korean 체크 후 -> Generate Locales: 해당 언어 생성 진행 -> 생성된 언어 확인 후 Create로 한글, 영어를 참조한 Uitexts 문자열 테이블 생성

상단에 있는 Edit Table Collection 클릭 -> 조금 전에 생성한 UiTexts(StringTable) 선택 후 key값, 각 나라별 Value값 설정

그리고 언어 선택을 할 수 있는 Dropdown에 LocaleDropdown.cs 적용

해당 스크립트 컴포넌트에 dropdown을 연결해준다.

// LocaleDropdown.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Localization.Settings;
using UnityEngine.UI;

public class LocaleDropdown : MonoBehaviour
{
    public Dropdown dropdown;

    IEnumerator Start()
    {
        // Wait for the localization system to initialize, loading Locales, preloading etc.
        yield return LocalizationSettings.InitializationOperation;

        // Generate list of available Locales
        var options = new List<Dropdown.OptionData>();
        int selected = 0;
        for (int i = 0; i < LocalizationSettings.AvailableLocales.Locales.Count; ++i)
        {
            var locale = LocalizationSettings.AvailableLocales.Locales[i];
            if (LocalizationSettings.SelectedLocale == locale)
                selected = i;
            options.Add(new Dropdown.OptionData(locale.name));
        }
        dropdown.options = options;

        dropdown.value = selected;
        dropdown.onValueChanged.AddListener(LocaleSelected);
    }

    static void LocaleSelected(int index)
    {
        LocalizationSettings.SelectedLocale = LocalizationSettings.AvailableLocales.Locales[index];
    }
}

그리고 Text 요소를 현지화 적용을 위해 Localize String Event 컴포넌트를 추가 후 키 값을 설정한다.

Dropdown 요소를 현지화 적용하려면 추가 스크립트 LocalizeDropdown.cs를 적용한다.

적용 후 변경을 원하는 각 Dropdown의 Value값을 설정한다.

// LocalizeDropdown.cs

using System;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine;
using UnityEngine.Localization;
using UnityEngine.Localization.Settings;

namespace UVH.Localization
{
    [RequireComponent(typeof(Dropdown))]
    [AddComponentMenu("Localization/Localize Dropdown")]
    public class LocalizeDropdown : MonoBehaviour
    {

        [Serializable]
        public class LocalizedDropdownOption
        {
            public LocalizedString text;

            public LocalizedSprite sprite; //not implemented yet!
        }

        public List<LocalizedDropdownOption> options;
        public int selectedOptionIndex = 0;
        private Locale currentLocale = null;
        private Dropdown Dropdown => GetComponent<Dropdown>();


        private void Start()
        {
            getLocale();
            UpdateDropdown(currentLocale);
            LocalizationSettings.SelectedLocaleChanged += UpdateDropdown;
        }


        private void OnEnable() => LocalizationSettings.SelectedLocaleChanged += UpdateDropdown;
        private void OnDisable() => LocalizationSettings.SelectedLocaleChanged -= UpdateDropdown;
        void OnDestroy() => LocalizationSettings.SelectedLocaleChanged -= UpdateDropdown;



        private void getLocale()
        {
            var locale = LocalizationSettings.SelectedLocale;
            if (currentLocale != null && locale != currentLocale)
            {
                currentLocale = locale;
            }
        }


        private void UpdateDropdown(Locale locale)
        {
            selectedOptionIndex = Dropdown.value;
            Dropdown.ClearOptions();

            for (int i = 0; i < options.Count; i++)
            {
                //sprite functionality isn't ready yet.
                Sprite localizedSprite = null;

                String localizedText = options[i].text.GetLocalizedString();
                Dropdown.options.Add(new Dropdown.OptionData(localizedText, localizedSprite));
            }

            Dropdown.value = selectedOptionIndex;
            Dropdown.RefreshShownValue();
        }

    }
}

플레이 후 현지화 기능이 정상 동작하는지 확인한다.

 

결론

두 기능 모두 다른 개발자분께서 열심히 작성해주신 글들을 참고하여서 완성한 기능입니다.

아래 참고 문헌을 확인하시면 영상 설명도 있어서 공부에 더 도움이 될 것으로 생각합니다.

처음 구현하게 된 현지화 기능이었지만 필자는 구글 스프레드 시트를 이용할 예정입니다.
개발 직군 외에도 접근이 편한 요소인 것이 장점이라고 생각합니다.

이상 긴 글 읽어주셔서 감사합니다 :)

참고 문헌

고라니TV - 게임개발 채널 | 다국어 번역 자동화 이 영상으로 끝.

https://velog.io/@uchang903/유니티-다중-언어-지원로컬라이징Localization 

TheRealProfessionalGamer | Easy to Use Unity Localization - Built in.

https://forum.unity.com/threads/localizing-ui-dropdown-options.896951/