302 lines
14 KiB
C#
302 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using UnityEngine;
|
|
using UVC.Json;
|
|
using UVC.Log;
|
|
|
|
namespace UVC.Locale
|
|
{
|
|
/// <summary>
|
|
/// 다국어 데이터를 관리하고, 현재 설정된 언어에 맞는 번역 문자열을 제공하는 클래스입니다.
|
|
/// JSON 파일을 로드하여 다국어 데이터를 초기화하며, 언어 변경 시 이벤트를 통해 알림을 제공합니다.
|
|
/// 이 클래스는 싱글톤으로 구현되어 어디서든 <see cref="Instance"/>를 통해 접근할 수 있습니다.
|
|
/// </summary>
|
|
/// <example>
|
|
/// <code>
|
|
/// // 1. LocalizationManager 인스턴스 접근 (싱글톤)
|
|
/// // LocalizationManager locManager = LocalizationManager.Instance; // 직접 인스턴스화 대신 Instance 사용
|
|
///
|
|
/// // 2. (선택 사항) 언어 변경 이벤트 구독
|
|
/// LocalizationManager.Instance.OnLanguageChanged += (newLanguage) => {
|
|
/// ULog.Debug($"언어가 {newLanguage}(으)로 변경되었습니다.");
|
|
/// // UI 업데이트 로직 등
|
|
/// };
|
|
///
|
|
/// // 3. 다국어 데이터 로드 (StreamingAssets 폴더의 locale.json 파일을 기본으로 로드)
|
|
/// // 인스턴스 생성 시 자동으로 로드하거나, 별도의 초기화 메서드를 통해 로드할 수 있습니다.
|
|
/// // 예시: 로드 메서드가 private 생성자 또는 별도 초기화 메서드에서 호출된다고 가정
|
|
/// if (LocalizationManager.Instance.LoadDefaultLocalizationData()) // LoadDefaultLocalizationData가 public이라고 가정
|
|
/// {
|
|
/// ULog.Debug("다국어 데이터 로드 성공!");
|
|
///
|
|
/// // 4. 사용 가능한 언어 목록 확인
|
|
/// List<string> availableLangs = LocalizationManager.Instance.AvailableLanguages;
|
|
/// ULog.Debug("사용 가능한 언어: " + string.Join(", ", availableLangs));
|
|
///
|
|
/// // 5. 언어 변경
|
|
/// LocalizationManager.Instance.SetCurrentLanguage("ko-KR"); // 한국어로 변경
|
|
///
|
|
/// // 6. 번역된 문자열 가져오기
|
|
/// string welcomeMsg = LocalizationManager.Instance.GetString("welcome_message");
|
|
/// ULog.Debug(welcomeMsg); // 출력 (한국어): 환영합니다!
|
|
///
|
|
/// LocalizationManager.Instance.SetCurrentLanguage("en-US"); // 영어로 변경
|
|
/// welcomeMsg = LocalizationManager.Instance.GetString("welcome_message");
|
|
/// ULog.Debug(welcomeMsg); // 출력 (영어): Welcome!
|
|
/// }
|
|
/// else
|
|
/// {
|
|
/// ULog.Error("다국어 데이터 로드 실패.");
|
|
/// }
|
|
/// </code>
|
|
/// </example>
|
|
public class LocalizationManager
|
|
{
|
|
#region singleton
|
|
|
|
// 싱글톤 인스턴스를 저장하기 위한 private static 변수
|
|
private static LocalizationManager _instance;
|
|
// 멀티스레드 환경에서의 동시 접근을 제어하기 위한 lock 객체
|
|
private static readonly object _lock = new object();
|
|
|
|
/// <summary>
|
|
/// <see cref="LocalizationManager"/>의 싱글톤 인스턴스를 가져옵니다.
|
|
/// 인스턴스가 아직 생성되지 않았다면 새로 생성하여 반환합니다.
|
|
/// </summary>
|
|
public static LocalizationManager Instance
|
|
{
|
|
get
|
|
{
|
|
// Double-checked locking으로 스레드 안전성 확보
|
|
if (_instance == null)
|
|
{
|
|
lock (_lock)
|
|
{
|
|
if (_instance == null)
|
|
{
|
|
_instance = new LocalizationManager("ko-KR");
|
|
_instance.LoadDefaultLocalizationData(); // 기본 언어 데이터 로드
|
|
}
|
|
}
|
|
}
|
|
return _instance;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
private LocalizationDataSource _dataSource;
|
|
private string _currentLanguage;
|
|
|
|
/// <summary>
|
|
/// 언어가 변경되었을 때 발생하는 이벤트의 델리게이트입니다.
|
|
/// </summary>
|
|
/// <param name="newLanguageCode">새롭게 설정된 언어 코드입니다 (예: "en-US", "ko-KR").</param>
|
|
public delegate void LanguageChangedHandler(string newLanguageCode);
|
|
/// <summary>
|
|
/// 현재 언어가 <see cref="SetCurrentLanguage"/> 메서드를 통해 성공적으로 변경되었을 때 발생하는 이벤트입니다.
|
|
/// </summary>
|
|
public event LanguageChangedHandler OnLanguageChanged;
|
|
|
|
/// <summary>
|
|
/// 현재 설정된 언어 코드를 가져옵니다.
|
|
/// </summary>
|
|
public string CurrentLanguage => _currentLanguage;
|
|
|
|
/// <summary>
|
|
/// 로드된 다국어 데이터에서 사용 가능한 모든 언어 코드 목록을 가져옵니다.
|
|
/// 데이터가 로드되지 않았거나 번역 데이터가 비어있으면 빈 리스트를 반환합니다.
|
|
/// </summary>
|
|
public List<string> AvailableLanguages
|
|
{
|
|
get
|
|
{
|
|
if (_dataSource.Translations != null)
|
|
{
|
|
return new List<string>(_dataSource.Translations.Keys);
|
|
}
|
|
return new List<string>(); // 데이터가 없을 경우 빈 리스트 반환
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// <see cref="LocalizationManager"/> 클래스의 새 인스턴스를 초기화합니다.
|
|
/// 기본 언어를 "ko-KR"로 설정합니다.
|
|
/// 생성자를 private으로 변경하여 외부에서의 직접적인 인스턴스 생성을 막습니다.
|
|
/// </summary>
|
|
private LocalizationManager(string defaultLanguage = "ko-KR")
|
|
{
|
|
_dataSource = new LocalizationDataSource();
|
|
// 기본 언어를 설정하거나, 시스템 설정을 따르도록 초기화할 수 있습니다.
|
|
_currentLanguage = defaultLanguage; // 예시 기본 언어
|
|
}
|
|
|
|
/// <summary>
|
|
/// 기본 경로(<see cref="Application.streamingAssetsPath"/>/locale.json)에서 다국어 데이터를 로드합니다.
|
|
/// </summary>
|
|
/// <returns>데이터 로드 성공 시 true, 실패 시 false를 반환합니다.</returns>
|
|
public bool LoadDefaultLocalizationData()
|
|
{
|
|
string path = Path.Combine(Application.streamingAssetsPath, "locale.json");
|
|
return LoadLocalizationData(path);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 지정된 파일 경로에서 JSON 형식의 다국어 데이터를 로드합니다.
|
|
/// </summary>
|
|
/// <param name="filePath">로드할 JSON 파일의 전체 경로입니다.</param>
|
|
/// <returns>데이터 로드 및 분석 성공 시 true, 실패 시 false를 반환합니다.</returns>
|
|
public bool LoadLocalizationData(string filePath)
|
|
{
|
|
if (!File.Exists(filePath))
|
|
{
|
|
ULog.Error($"Localization file not found: {filePath}", new System.Exception($"Localization file not found: {filePath}"));
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
string jsonData = File.ReadAllText(filePath);
|
|
// 직접 Dictionary로 역직렬화한 후 Translations에 할당
|
|
var translationsData = JsonHelper.FromJson<Dictionary<string, Dictionary<string, string>>>(jsonData);
|
|
|
|
if (translationsData == null)
|
|
{
|
|
ULog.Error($"Failed to deserialize localization data or no translations found in: {filePath}");
|
|
_dataSource = new LocalizationDataSource(); // 빈 데이터 소스로 초기화
|
|
return false;
|
|
}
|
|
|
|
_dataSource.Translations = translationsData; // 읽어온 데이터를 Translations 프로퍼티에 할당
|
|
|
|
// 데이터 로드 후, 현재 설정된 언어가 유효한지 확인하고,
|
|
// 유효하지 않다면 사용 가능한 첫 번째 언어나 기본 언어로 설정할 수 있습니다.
|
|
if (!string.IsNullOrEmpty(_currentLanguage) && !_dataSource.Translations.ContainsKey(_currentLanguage))
|
|
{
|
|
ULog.Warning($"Current language '{_currentLanguage}' not found in the new data source. Attempting to set to default or first available.");
|
|
if (AvailableLanguages.Count > 0)
|
|
{
|
|
SetCurrentLanguage(AvailableLanguages[0]); // 사용 가능한 첫 번째 언어로 설정
|
|
}
|
|
// else: 사용 가능한 언어가 없으면 변경하지 않음 (또는 특정 기본값으로 설정)
|
|
}
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ULog.Error($"Error loading localization data from {filePath}: {ex.Message}", ex);
|
|
_dataSource = new LocalizationDataSource(); // 오류 발생 시 빈 데이터 소스로 초기화
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 현재 사용 언어를 변경합니다.
|
|
/// 성공적으로 언어가 변경되면 <see cref="OnLanguageChanged"/> 이벤트가 발생합니다.
|
|
/// </summary>
|
|
/// <param name="languageCode">설정할 언어 코드입니다 (예: "en-US", "ko-KR").</param>
|
|
public void SetCurrentLanguage(string languageCode)
|
|
{
|
|
if (string.IsNullOrEmpty(languageCode))
|
|
{
|
|
ULog.Warning("Language code cannot be null or empty.");
|
|
return;
|
|
}
|
|
|
|
if (_dataSource?.Translations == null)
|
|
{
|
|
ULog.Warning("Translations data source is not loaded. Cannot set language.");
|
|
return;
|
|
}
|
|
|
|
if (_dataSource.Translations.ContainsKey(languageCode))
|
|
{
|
|
if (_currentLanguage != languageCode)
|
|
{
|
|
_currentLanguage = languageCode;
|
|
OnLanguageChanged?.Invoke(_currentLanguage); // 언어가 성공적으로 변경되면 이벤트 발생
|
|
ULog.Debug($"Current language changed to: {languageCode}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ULog.Warning($"Language '{languageCode}' not found in data source. Current language remains '{_currentLanguage}'.");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 현재 설정된 언어를 기준으로, 주어진 키에 해당하는 번역된 문자열을 가져옵니다.
|
|
/// </summary>
|
|
/// <param name="key">번역 문자열을 찾기 위한 키입니다.</param>
|
|
/// <returns>
|
|
/// 번역된 문자열을 반환합니다.
|
|
/// 키가 null이거나 비어있으면 빈 문자열을 반환합니다.
|
|
/// 해당 키 또는 현재 언어에 대한 번역을 찾을 수 없으면, "[키]" 형식의 문자열을 반환합니다.
|
|
/// </returns>
|
|
public string GetString(string key)
|
|
{
|
|
if (string.IsNullOrEmpty(key))
|
|
{
|
|
ULog.Warning("Requested localization key is null or empty.");
|
|
return string.Empty;
|
|
}
|
|
|
|
if (_dataSource?.Translations == null)
|
|
{
|
|
ULog.Warning("Translations data source is not loaded. Cannot get string for key: " + key);
|
|
return key;
|
|
}
|
|
|
|
if (_dataSource.Translations.TryGetValue(_currentLanguage, out var languageSpecificStrings) &&
|
|
languageSpecificStrings.TryGetValue(key, out var translatedString))
|
|
{
|
|
return translatedString;
|
|
}
|
|
|
|
ULog.Debug($"Translation for key '{key}' not found in language '{_currentLanguage}'.");
|
|
return key; // 키를 그대로 반환
|
|
}
|
|
|
|
/// <summary>
|
|
/// 특정 언어의 특정 키에 대한 번역된 문자열을 직접 가져옵니다.
|
|
/// </summary>
|
|
/// <param name="languageCode">문자열을 가져올 대상 언어 코드입니다.</param>
|
|
/// <param name="key">번역 문자열을 찾기 위한 키입니다.</param>
|
|
/// <returns>
|
|
/// 번역된 문자열을 반환합니다.
|
|
/// 언어 코드나 키가 null이거나 비어있으면 빈 문자열을 반환합니다.
|
|
/// 해당 언어 또는 키에 대한 번역을 찾을 수 없으면 null을 반환합니다.
|
|
/// </returns>
|
|
public string? GetString(string languageCode, string key)
|
|
{
|
|
if (string.IsNullOrEmpty(languageCode))
|
|
{
|
|
ULog.Warning("Requested language code is null or empty for GetString(languageCode, key).");
|
|
return null;
|
|
}
|
|
if (string.IsNullOrEmpty(key))
|
|
{
|
|
ULog.Warning("Requested localization key is null or empty for GetString(languageCode, key).");
|
|
return null;
|
|
}
|
|
|
|
if (_dataSource?.Translations == null)
|
|
{
|
|
ULog.Warning("Translations data source is not loaded. Cannot get string for key: " + key + " in language: " + languageCode);
|
|
return null;
|
|
}
|
|
|
|
if (_dataSource.Translations.TryGetValue(languageCode, out var languageSpecificStrings) &&
|
|
languageSpecificStrings.TryGetValue(key, out var translatedString))
|
|
{
|
|
return translatedString;
|
|
}
|
|
|
|
ULog.Debug($"Translation for key '{key}' not found in language '{languageCode}'.");
|
|
return null; // 해당 언어/키가 없음을 표시 (이전에는 "[languageCode:key]" 반환)
|
|
}
|
|
}
|
|
}
|