[TIL] Unity - Localization Refactoring 회고 - day 80

뭉크의 개발·2023년 11월 9일
0

Unity - Camp

목록 보기
49/70

🐧 들어가기 앞서

게임을 제작하다보면 유니티에서 여러가지 패키지들이 사용된다.

그러나 내가 사용하려고 하는 패키지에 대해 이해가 필요하지만 예제가 존재하지 않아 패키지를 모르고 쓰는 경우가 많다.

유니티는 이런 사용자를 위해 samples를 재공하는 패키지가 존재한다.

  • Localization의 Samples

해당 사진을 보면 여러가지 상황에 대한 패키지를 Import 하게 지원해주고 있다.


🐧 오늘 배운 것

언어 기능을 구현할 때 설정창에서 드롭다운으로 언어를 변경할 수 있게 구현하고 싶었다.

그때 Samples를 발견하게 됐는데, 위 사진에서 UGUI 샘플이 존재함을 알 수 있었다.

샘플에서 코드를 가져와 이해하고 코드를 그대로 사용했는데

다시보니 조금 리팩토링이 필요해 가져왔다.

기존 코드의 개선점은 다음과 같다.

  1. 중복 코드 제거
    InitializeCompleted 메서드에서 언어 옵션을 추가하는 부분과 LocalizationSettings_SelectedLocaleChanged 메서드에서 선택된 언어를 업데이트하는 부분이 비슷하다. 이를 개선하기 위해 공통 로직을 별도의 메서드로 추출할 수 있다.

  2. 비동기 처리 개선
    비동기 작업의 완료 처리를 이벤트 구독/해제를 통해 수행하고 있다. 이벤트 해제를 누락하는 오류가 발생할 수 있으므로 주의가 필요해보였다.

  3. 메서드 이름 규칙
    이벤트 핸들러 메서드의 이름은 일반적으로 관련 이벤트를 반영하도록 지어야 한다.

  4. 안전성 향상
    OnSelectionChanged 메서드에서 선택된 로케일을 설정할 때 인덱스가 유효한 범위 내에 있는지 확인하는 것이 좋다.


🐧 기억할 것 & 진행

기존 코드

// ...

void InitializeCompleted(AsyncOperationHandle obj)
{
    var options = new List<string>();
    int selectedOption = 0;
    var locales = LocalizationSettings.AvailableLocales.Locales;
    for (int i = 0; i < locales.Count; ++i)
    {
        var locale = locales[i];
        if (LocalizationSettings.SelectedLocale == locale)
            selectedOption = i;

        var displayName = locale.Identifier.CultureInfo.NativeName;
        options.Add(displayName);
    }

    // ...
}

void OnSelectionChanged(int index)
{
    LocalizationSettings.SelectedLocaleChanged -= LocalizationSettings_SelectedLocaleChanged;

    // ...
}

void LocalizationSettings_SelectedLocaleChanged(Locale locale)
{
    // ...
}

리팩토링 코드

// ...

void PopulateLanguageOptions()
{
    languageDropdown.ClearOptions();

    var options = new List<string>();
    int selectedOption = GetLocaleOptions(options);

    // ...
}

int GetLocaleOptions(List<string> options)
{
    // ...
}

void OnSelectionChanged(int index)
{
    if (index >= 0 && index < LocalizationSettings.AvailableLocales.Locales.Count)
    {
        LocalizationSettings.SelectedLocaleChanged -= OnSelectedLocaleChanged;

        // ... 
    }
    else
    {
        Debug.LogWarning("Selected language index is out of range.");
    }
}

void OnSelectedLocaleChanged(Locale locale)
{
    // ...
}

🐧 전체 코드 비교

기존 코드

using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization;
using UnityEngine.ResourceManagement.AsyncOperations;

public class UILanguageOption : MonoBehaviour
{
    [SerializeField] private TMP_Dropdown languageDropdown;
    AsyncOperationHandle initializeOperation;

    void Start()
    {
        languageDropdown.onValueChanged.AddListener(OnSelectionChanged);

        languageDropdown.ClearOptions();
        languageDropdown.options.Add(new TMP_Dropdown.OptionData("Loading..."));
        languageDropdown.interactable = false;

        initializeOperation = LocalizationSettings.SelectedLocaleAsync;
        if (initializeOperation.IsDone)
        {
            InitializeCompleted(initializeOperation);
        }
        else
        {
            initializeOperation.Completed += InitializeCompleted;
        }
    }

    void InitializeCompleted(AsyncOperationHandle obj)
    {
        var options = new List<string>();
        int selectedOption = 0;
        var locales = LocalizationSettings.AvailableLocales.Locales;
        for (int i = 0; i < locales.Count; ++i)
        {
            var locale = locales[i];
            if (LocalizationSettings.SelectedLocale == locale)
                selectedOption = i;

            var displayName = locales[i].Identifier.CultureInfo != null ? locales[i].Identifier.CultureInfo.NativeName : locales[i].ToString();
            options.Add(displayName);
        }

        if (options.Count == 0)
        {
            options.Add("No Locales Available");
            languageDropdown.interactable = false;
        }
        else
        {
            languageDropdown.interactable = true;
        }

        languageDropdown.ClearOptions();
        languageDropdown.AddOptions(options);
        languageDropdown.SetValueWithoutNotify(selectedOption);

        LocalizationSettings.SelectedLocaleChanged += LocalizationSettings_SelectedLocaleChanged;
    }

    void OnSelectionChanged(int index)
    {
        LocalizationSettings.SelectedLocaleChanged -= LocalizationSettings_SelectedLocaleChanged;

        var locale = LocalizationSettings.AvailableLocales.Locales[index];
        LocalizationSettings.SelectedLocale = locale;

        LocalizationSettings.SelectedLocaleChanged += LocalizationSettings_SelectedLocaleChanged;
    }

    void LocalizationSettings_SelectedLocaleChanged(Locale locale)
    {
        var selectedIndex = LocalizationSettings.AvailableLocales.Locales.IndexOf(locale);
        languageDropdown.SetValueWithoutNotify(selectedIndex);
    }
}

개선 코드

using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.Localization.Settings;
using UnityEngine.Localization;
using UnityEngine.ResourceManagement.AsyncOperations;

public class UILanguageOption : MonoBehaviour
{
    [SerializeField] private TMP_Dropdown languageDropdown;

    void Start()
    {
        InitializeDropdown();
    }

    void InitializeDropdown()
    {
        languageDropdown.onValueChanged.AddListener(OnSelectionChanged);
        languageDropdown.ClearOptions();
        languageDropdown.options.Add(new TMP_Dropdown.OptionData("Loading..."));
        languageDropdown.interactable = false;

        var initializeOperation = LocalizationSettings.SelectedLocaleAsync;
        if (initializeOperation.IsDone)
        {
            PopulateLanguageOptions();
        }
        else
        {
            initializeOperation.Completed += (operation) => PopulateLanguageOptions();
        }
    }

    void PopulateLanguageOptions()
    {
        languageDropdown.ClearOptions();

        var options = new List<string>();
        int selectedOption = GetLocaleOptions(options);

        if (options.Count == 0)
        {
            languageDropdown.options.Add(new TMP_Dropdown.OptionData("No Locales Available"));
            languageDropdown.interactable = false;
        }
        else
        {
            languageDropdown.AddOptions(options);
            languageDropdown.SetValueWithoutNotify(selectedOption);
            languageDropdown.interactable = true;
        }

        LocalizationSettings.SelectedLocaleChanged += OnSelectedLocaleChanged;
    }

    int GetLocaleOptions(List<string> options)
    {
        var locales = LocalizationSettings.AvailableLocales.Locales;
        int selectedOption = 0;

        for (int i = 0; i < locales.Count; ++i)
        {
            var locale = locales[i];
            var displayName = locale.Identifier.CultureInfo.NativeName;
            options.Add(displayName);

            if (LocalizationSettings.SelectedLocale == locale)
                selectedOption = i;
        }

        return selectedOption;
    }

    void OnSelectionChanged(int index)
    {
        if (index >= 0 && index < LocalizationSettings.AvailableLocales.Locales.Count)
        {
            LocalizationSettings.SelectedLocaleChanged -= OnSelectedLocaleChanged;

            var locale = LocalizationSettings.AvailableLocales.Locales[index];
            LocalizationSettings.SelectedLocale = locale;

            LocalizationSettings.SelectedLocaleChanged += OnSelectedLocaleChanged;
        }
        else
        {
            Debug.LogWarning("Selected language index is out of range.");
        }
    }

    void OnSelectedLocaleChanged(Locale locale)
    {
        int selectedIndex = LocalizationSettings.AvailableLocales.Locales.IndexOf(locale);
        languageDropdown.SetValueWithoutNotify(selectedIndex);
    }
}

🐧 느낀점

기존 코드는 내가 제작한 코드가 아니기 때문에 가독성도 떨어졌고, 무엇을 하고 있는지 역할을 알아보기 힘들었다.

물론 너무나 잘 작성되어있던 코드였다.

개선한 코드는 최대한 역할을 분담했고 코드의 명확성과 재사용성을 지키기 위해 수정했다.

타인의 코드를 이해하면서 내 것으로 만들기 위해 노력해 기분이 좋았다!

0개의 댓글