유니티에서 다국어 로컬라이제이션을 구현하는 시스템입니다. CSV 파일을 기반으로 텍스트 데이터를 관리하며, TextMeshProUGUI와 연동해 동적 텍스트 변경을 지원합니다.
동적 언어 전환
- 자동 감지: 디바이스 언어를 감지해 초기 설정
- 수동 설정: kr(한국어), en(영어), jp(일본어) 등 원하는 언어로 전환 가능
CSV 파일 기반 데이터 관리
- 다국어 데이터를 CSV 파일로 효율적으로 관리
- 헤더를 기준으로 언어 매핑, 키-번역 문자열 로드
유니티와의 통합
- TextMeshProUGUI 컴포넌트와 연동
- 매개변수를 포함한 텍스트 변환 지원 ({0}, {1} 등)
언어 변경 이벤트
- 언어 변경 시 콜백(Event)을 통해 UI 업데이트
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using TMPro;
public enum LanguageOption
{
DeviceLanguage, kr, en, jp,
}
public class Localization
{
private const string LOCALIZATION_FILE_NAME = "LOCALIZATION_COMMON";
private TextAsset _localSheet;
private LanguageOption _language = LanguageOption.DeviceLanguage;
private readonly Dictionary<string, string> _localizedTextDics = new();
public event Action LanguageOptionChanged;
public string CurrentLanguageKey { get; private set; }
public void Init()
{
CurrentLanguageKey = _language == LanguageOption.DeviceLanguage ? GetSystemLanguageKey() : _language.ToString();
Debug.Log($"[Localization] Current Language is set to {CurrentLanguageKey}");
LoadLocalizationData();
}
public void ChangeLanguageOption(LanguageOption targetLanguage)
{
if (_language == targetLanguage) return;
_language = targetLanguage;
Init();
LanguageOptionChanged?.Invoke();
}
public void ApplyLocalization(TextMeshProUGUI text, string key) => text.text = GetLocalizedText(key);
public void ApplyLocalizationWithParam(TextMeshProUGUI text, string key, params object[] args) => text.text = GetLocalizedTextWithParam(key, args);
public string GetLocalizedText(string key)
{
return _localizedTextDics.TryGetValue(key, out string text) ? text : $"[Missing: {key}]";
}
public string GetLocalizedTextWithParam(string key, params object[] args)
{
string text = GetLocalizedText(key);
return string.IsNullOrEmpty(text) ? text : string.Format(text, args);
}
private void LoadLocalizationData()
{
_localizedTextDics.Clear();
LoadFromCSV(Resources.Load<TextAsset>(LOCALIZATION_FILE_NAME));
if (_localSheet != null)
{
LoadFromCSV(_localSheet);
}
}
private void LoadFromCSV(TextAsset textAsset)
{
if (textAsset == null) return;
string[] lines = textAsset.text.Split('\n');
if (lines.Length < 2)
{
Debug.LogWarning("[Localization]: CSV file is empty or contains only headers.");
return;
}
string[] headers = lines[0].Split(',');
int targetLanguageIndex = Array.IndexOf(headers, CurrentLanguageKey);
if (targetLanguageIndex == -1)
{
Debug.LogWarning($"[Localization]: Language key \"{CurrentLanguageKey}\" not found in CSV.");
return;
}
for (int i = 1; i < lines.Length; i++)
{
string line = lines[i];
if (string.IsNullOrEmpty(line)) continue;
string[] columns = Regex.Split(line, ",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");
if (columns.Length <= targetLanguageIndex) continue;
string key = columns[0].Trim('"', ' ');
string value = columns[targetLanguageIndex].Trim('"', ' ').Replace("<br>", "\n");
if (!string.IsNullOrEmpty(key) && !string.IsNullOrEmpty(value))
{
_localizedTextDics[key] = value;
}
}
}
private string GetSystemLanguageKey()
{
return Application.systemLanguage switch
{
SystemLanguage.Korean => nameof(LanguageOption.kr),
SystemLanguage.English => nameof(LanguageOption.en),
SystemLanguage.Japanese => nameof(LanguageOption.jp),
_ => nameof(LanguageOption.en)
};
}
}
- Init: 로컬라이제이션 초기화 및 CSV 데이터 로드.
- ChangeLanguageOption: 언어 변경 및 이벤트 트리거.
- ApplyLocalization: 특정 키의 번역 텍스트를 TextMeshProUGUI에 적용.
- ApplyLocalizationWithParam: 매개변수를 포함한 텍스트를 적용.
사용 예시: TextMeshProUGUI 컴포넌트와 함께 (파라미터 미사용)
using UnityEngine;
using TMPro;
public class LocalizationTest : MonoBehaviour
{
// 로컬라이제이션 인스턴스
private Localization localization;
// TextMeshProUGUI 컴포넌트 참조
[SerializeField] private TextMeshProUGUI helloText;
[SerializeField] private TextMeshProUGUI goodbyeText;
void Start()
{
// Localization 초기화
localization = new Localization();
localization.Init();
// 이벤트 핸들러 등록 (언어 변경 시 호출)
localization.LanguageOptionChanged += OnLanguageChanged;
// 초기 텍스트 설정
UpdateLocalizedTexts();
}
/// <summary>
/// 언어 변경 버튼 클릭 시 호출
/// </summary>
public void OnChangeLanguageToKorean()
{
localization.ChangeLanguageOption(LanguageOption.kr); // 한국어로 변경
}
public void OnChangeLanguageToEnglish()
{
localization.ChangeLanguageOption(LanguageOption.en); // 영어로 변경
}
public void OnChangeLanguageToJapanese()
{
localization.ChangeLanguageOption(LanguageOption.jp); // 일본어로 변경
}
/// <summary>
/// 언어 변경 시 UI 업데이트
/// </summary>
private void OnLanguageChanged()
{
Debug.Log("Language changed. Updating texts...");
UpdateLocalizedTexts();
}
/// <summary>
/// UI 텍스트를 로컬라이제이션 데이터로 업데이트
/// </summary>
private void UpdateLocalizedTexts()
{
localization.ApplyLocalization(helloText, "HELLO");
localization.ApplyLocalization(goodbyeText, "GOODBYE");
}
}
CSV
Key,kr,en,jp
HELLO,안녕하세요,Hello,こんにちは
GOODBYE,안녕히 가세요,Goodbye,さようなら
사용 예시2: 정보만 가져오기 (파라미터 사용)
using UnityEngine;
public class LocalizationParamTest : MonoBehaviour
{
// 로컬라이제이션 인스턴스
private Localization localization;
void Start()
{
// Localization 초기화
localization = new Localization();
localization.Init();
// 테스트: 파라미터를 포함한 텍스트 가져오기
string helloText = localization.GetLocalizedTextWithParam("HELLO_NAME", "John");
Debug.Log(helloText); // 출력 예: "Hello, John" (영어), "안녕하세요, John" (한국어)
string scoreText = localization.GetLocalizedTextWithParam("SCORE_MESSAGE", 12345);
Debug.Log(scoreText); // 출력 예: "Your score is 12345" (영어), "당신의 점수는 12345입니다" (한국어)
}
}
CSV
Key,kr,en,jp
HELLO_NAME,안녕하세요, {0},Hello, {0},こんにちは, {0}
SCORE_MESSAGE,당신의 점수는 {0}입니다,Your score is {0},あなたのスコアは{0}です
주의 사항
- CSV 파일 위치: Assets/Resources/LOCALIZATION_COMMON.csv
- 폰트 호환성: 사용 중인 폰트가 각 언어를 지원하는지 확인