본문 바로가기
Devlog/Unity

[Tip] 유니티 Text 다국어 설정 자동화(String Reference 자동 탐지)

by Dev-WhaleShark 2025. 8. 16.

다국어 작업 중 이미 기존에 한국어로 된 Text UI에서 하나씩 String Reference를 Table에서 찾아 넣기 귀찮아서 간단하게 자동 탐지 되게끔 만들었던 코드입니다.

using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Localization.Components;

namespace UnityEditor.Localization.Plugins.TMPro {
    [InitializeOnLoad]
    internal static class LocalizeComponentAuto_TMPro {
        static LocalizeComponentAuto_TMPro() {
        }

        //TMP Text에 LocalizeAuto 메뉴 항목을 추가
        [MenuItem( "CONTEXT/TextMeshProUGUI/LocalizeAuto" )]
        static void LocalizeTMProText( MenuCommand command ) {
            var target = command.context as TextMeshProUGUI;
            SetupForLocalization( target );
        }


        /// <summary>
        /// TMP_Text를 가진 Object에 LocalizeStringEvent를 자동으로 추가하고
        /// 다국어 테이블을 순회하여 현재 Text의 Value와 동일한 값을 가진 Entry의 키로
        /// String Reference를 자동 할당
        /// </summary>
        /// <param name="target"></param>
        public static MonoBehaviour SetupForLocalization( TextMeshProUGUI target ) {
            LocalizeStringEvent localizeStringEvent = target.GetComponent<LocalizeStringEvent>();

            //LocalizeStringEvent 없으면 자동 추가
            if ( localizeStringEvent == null ) {
                localizeStringEvent = Undo.AddComponent( target.gameObject, typeof( LocalizeStringEvent ) ) as LocalizeStringEvent;
            }

            //Reflection으로 TMP_Text의 text 프로퍼티의 setter 메서드 정보 얻기
            var textProperty = typeof( TMP_Text ).GetProperty( nameof( TMP_Text.text ), System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public );
            var setStringMethod = textProperty.GetSetMethod();

            if(setStringMethod == null) {
                Debug.LogError( $"{target.GetType().Name}에서 'text' 프로퍼티의 setter 메서드를 찾을 수 없습니다.", target );
                return localizeStringEvent;
            }

            //기존에 Update String에 이벤트가 등록되어 있는지 확인(중복 방지)
            if ( !HasTextListener( localizeStringEvent.OnUpdateString, target, setStringMethod.Name ) ) {
                //메서드 델리게이트 생성 및 등록
                var methodDelegate = System.Delegate.CreateDelegate( typeof( UnityAction<string> ), target, setStringMethod ) as UnityAction<string>;
                
                //https://docs.unity3d.com/ScriptReference/Events.UnityEventTools.AddPersistentListener.html
                //AddListener는 런타임용 런타임이 아닌경우 적용되지 않음
                //AddPersistentListener를 통해 에디터에서도 할당
                Events.UnityEventTools.AddPersistentListener( localizeStringEvent.OnUpdateString, methodDelegate );
                
                //추가된 Listener을 Editor,Runtime 모두 동작하도록 설정
                localizeStringEvent.OnUpdateString.SetPersistentListenerState(
                    localizeStringEvent.OnUpdateString.GetPersistentEventCount() - 1,
                    UnityEventCallState.EditorAndRuntime
                );

                EditorUtility.SetDirty( localizeStringEvent );
            }

            //String Reference 자동 설정
            // LocalizeStringEvent의 StringReference가 비어있으면 현재 Text의 text를 기준으로 자동으로 찾기
            if ( localizeStringEvent.StringReference.IsEmpty ) {

                var strRef = LocalizationUtil.AutoFindLocalizedStringRef( target.text );

                if ( strRef == null ) {
                    Debug.LogWarning( $"Text : [<b>{target.text}</b>] 와 일치하는 String Reference를 찾지 못했습니다." );
                } else {
                    localizeStringEvent.StringReference = strRef;
                    EditorUtility.SetDirty( localizeStringEvent );
                }
            }

            return localizeStringEvent;
        }


        private static bool HasTextListener( UnityEvent<string> unityEvent, TextMeshProUGUI target, string expectedMethodName ) {
            int listenerCount = unityEvent.GetPersistentEventCount();

            for ( int i = 0; i < listenerCount; i++ ) {
                // TextMeshProUGUI의 text setter 확인
                if ( unityEvent.GetPersistentTarget( i ) == target &&
                    unityEvent.GetPersistentMethodName( i ) == expectedMethodName ) {
                    return true;
                }
            }

            return false;
        }
    }
}

 

위쪽 코드는 LocalizeStringEvent 자동 할당, 리스터 중복 방지 코드이고 자동으로 탐지하는 부분은 아래 코드입니다.

//String Reference 자동 설정
// LocalizeStringEvent의 StringReference가 비어있으면 현재 Text의 text를 기준으로 자동으로 찾기
if ( localizeStringEvent.StringReference.IsEmpty ) {
    var strRef = LocalizationUtil.AutoFindLocalizedStringRef( target.text );
    if ( strRef == null ) {
        Debug.LogWarning( $"Text : [<b>{target.text}</b>] 와 일치하는 String Reference를 찾지 못했습니다." );
    } else {
        localizeStringEvent.StringReference = strRef;
        EditorUtility.SetDirty( localizeStringEvent );
    }
}

 

코드 중 LocalizationUtil.AutoFindLocalizedStringRef()는 별도 유틸리티 함수로 파둔 코드입니다.
단순하게 모든 다국어 테이블에서 localizedText 파라미터와 일치하는 LocalizedString을 찾아 반환해줍니다.

#if UNITY_EDITOR
    public static LocalizedString AutoFindLocalizedStringRef( string localizedText ) {
        // 초기화 확인
        if ( !LocalizationSettings.InitializationOperation.IsDone ) {
            Debug.LogWarning( "Localization not initialized!" );
            return null;
        }

        var strTableCollections = LocalizationEditorSettings.GetStringTableCollections();

        // 초기화된 StringTableCollection이 없는 경우
        if ( strTableCollections == null )
            return null;

        //모든 테이블 순회
        foreach ( var collection in strTableCollections ) {
            //각 테이블별 언어(locale)별로 순회
            foreach ( var entry in collection.StringTables ) {
                // 각 언어별로 해당 문자열(localizedText)이 있는지 확인
                var founded = entry.Values.FirstOrDefault(x => x.Value == localizedText);
                if ( founded != default && founded != null ) {
                    // 해당 문자열이 있는 경우 LocalizedString 반환
                    return new LocalizedString( collection.TableCollectionName, founded.Key );
                }
            }
        }

        // 문자열을 찾지 못한 경우 null 반환
        return null;
    }
#endif

 

테스트로 테이블 하나를 만들고 Entry도 하나 생성해줍니다.

 

TMP_Text 컴포넌트를 하나 만들어 준 뒤 "안녕 세상!"으로 설정하고

 

TMP_Text 컨텍스트 메뉴를 열어보면 맨 아래 LocalizeAuto가 새롭게 생겼습니다.
눌러서 실행 시 자동으로 Localize String Event가 추가되고 String Reference도 설정됩니다. 

아까 코드에서 HasTextListener쪽 코드가 해주는 역할이 Update String에 리스너 자동 할당과 중복 할당 방지 코드입니다.

이외에 다국어 자동화로 만들었던 cs 파일 한국어 탐지, 프리팹 및 Scene 한국어 탐지 등 각종 다국어 관련 자동화 팁도 향후 작성 해보겠습니다. :D