LanguageManager 추가
This commit is contained in:
@@ -0,0 +1,285 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
#endif
|
||||
using UnityEngine;
|
||||
|
||||
namespace Eden.AutoMorpher
|
||||
{
|
||||
public enum Language
|
||||
{
|
||||
Korean = 0,
|
||||
Japanese = 1,
|
||||
English = 2,
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LocalizationEntry
|
||||
{
|
||||
public string key;
|
||||
public string value;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LocalizationFile
|
||||
{
|
||||
public LocalizationEntry[] entries;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
[InitializeOnLoad]
|
||||
#endif
|
||||
public static class LanguageManager
|
||||
{
|
||||
private const string PREF_KEY_LANG = "EdenAutoMorpher_Language";
|
||||
|
||||
private const string PATH_KO = "Language/Language_ko";
|
||||
private const string PATH_JA = "Language/Language_ja";
|
||||
private const string PATH_EN = "Language/Language_en";
|
||||
|
||||
private static Language _currentLanguage = Language.Korean;
|
||||
|
||||
private static Dictionary<string, string> _koTable;
|
||||
private static Dictionary<string, string> _jaTable;
|
||||
private static Dictionary<string, string> _enTable;
|
||||
|
||||
public static Language CurrentLanguage
|
||||
{
|
||||
get => _currentLanguage;
|
||||
set
|
||||
{
|
||||
if (_currentLanguage == value) return;
|
||||
_currentLanguage = value;
|
||||
#if UNITY_EDITOR
|
||||
EditorPrefs.SetInt(PREF_KEY_LANG, (int)_currentLanguage);
|
||||
#else
|
||||
PlayerPrefs.SetInt(PREF_KEY_LANG, (int)_currentLanguage);
|
||||
PlayerPrefs.Save();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static LanguageManager()
|
||||
{
|
||||
// Editor 시작 시 언어 로드
|
||||
#if UNITY_EDITOR
|
||||
int saved = EditorPrefs.GetInt(PREF_KEY_LANG, (int)Language.Korean);
|
||||
#else
|
||||
int saved = PlayerPrefs.GetInt(PREF_KEY_LANG, (int)Language.Korean);
|
||||
#endif
|
||||
_currentLanguage = (Language)Mathf.Clamp(saved, 0, 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 언어에서 문자열 가져오기.
|
||||
/// key 없으면 fallback → 다른 언어 → 최종적으로 key 자체 반환.
|
||||
/// </summary>
|
||||
public static string Get(string key)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key))
|
||||
return string.Empty;
|
||||
|
||||
// 현재 언어 테이블에서 먼저 검색
|
||||
if (TryGetFromCurrentLanguage(key, out string value))
|
||||
return value;
|
||||
|
||||
return FormatKeyNotFound(key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 언어별로 다른 키를 쓰고 싶을 때 (번호 기반 등)도 가능하도록
|
||||
/// 번호를 바로 key 문자열로 변환해서 사용하는 버전.
|
||||
/// </summary>
|
||||
public static string GetById(int id)
|
||||
{
|
||||
// 예: "ID.1001" 이런 식으로 통일하면 관리가 편함
|
||||
string key = $"ID.{id}";
|
||||
return Get(key);
|
||||
}
|
||||
|
||||
public static void SetLanguage(Language lang)
|
||||
{
|
||||
CurrentLanguage = lang;
|
||||
}
|
||||
|
||||
// ================== 내부 구현 ==================
|
||||
|
||||
private static bool TryGetFromCurrentLanguage(string key, out string value)
|
||||
{
|
||||
var table = GetTable(_currentLanguage);
|
||||
if (table != null && table.TryGetValue(key, out value))
|
||||
return true;
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryGetFromOtherLanguages(string key, out string value)
|
||||
{
|
||||
// 현재 언어 제외한 나머지 순회
|
||||
foreach (Language lang in System.Enum.GetValues(typeof(Language)))
|
||||
{
|
||||
if (lang == _currentLanguage)
|
||||
continue;
|
||||
|
||||
var table = GetTable(lang);
|
||||
if (table != null && table.TryGetValue(key, out value))
|
||||
return true;
|
||||
}
|
||||
|
||||
value = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> GetTable(Language lang)
|
||||
{
|
||||
switch (lang)
|
||||
{
|
||||
case Language.Korean:
|
||||
if (_koTable == null)
|
||||
_koTable = LoadTable(PATH_KO);
|
||||
return _koTable;
|
||||
|
||||
case Language.Japanese:
|
||||
if (_jaTable == null)
|
||||
_jaTable = LoadTable(PATH_JA);
|
||||
return _jaTable;
|
||||
|
||||
case Language.English:
|
||||
if (_enTable == null)
|
||||
_enTable = LoadTable(PATH_EN);
|
||||
return _enTable;
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// fileName: "lang_ko", "lang_ja", "lang_en" 처럼 사용
|
||||
private static Dictionary<string, string> LoadTable(string fileName)
|
||||
{
|
||||
var dict = new Dictionary<string, string>();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
// 스크립트 기준 Language 폴더 아래의 json 경로
|
||||
string assetPath = GetLanguageJsonAssetPath(fileName);
|
||||
// Assets/... 형식 경로를 실제 디스크 경로로 변환
|
||||
string fullPath = Path.GetFullPath(assetPath);
|
||||
|
||||
if (!File.Exists(fullPath))
|
||||
{
|
||||
Debug.LogWarning($"[AutoMorpherLanguage] Localization file not found: {fullPath}");
|
||||
return dict;
|
||||
}
|
||||
|
||||
string jsonText;
|
||||
try
|
||||
{
|
||||
// ✅ 강제로 UTF-8 로 읽기 (BOM 유무 상관 없음)
|
||||
jsonText = File.ReadAllText(fullPath, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"[AutoMorpherLanguage] Failed to read localization file: {fullPath}\n{ex}");
|
||||
return dict;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var file = JsonUtility.FromJson<LocalizationFile>(jsonText);
|
||||
if (file?.entries != null)
|
||||
{
|
||||
foreach (var entry in file.entries)
|
||||
{
|
||||
if (string.IsNullOrEmpty(entry.key))
|
||||
continue;
|
||||
|
||||
dict[entry.key] = entry.value ?? string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogError($"[AutoMorpherLanguage] Failed to parse localization file: {fullPath}\n{ex}");
|
||||
}
|
||||
#else
|
||||
// 런타임 빌드에서 쓸 계획이 없으면 비워둬도 됨
|
||||
#endif
|
||||
return dict;
|
||||
}
|
||||
private static string FormatKeyNotFound(string key)
|
||||
{
|
||||
// 먼저 현재 언어의 "Error.KeyNotFound" 가져오기
|
||||
if (TryGetFromCurrentLanguage("Error.KeyNotFound", out string template))
|
||||
return template.Replace("{0}", key);
|
||||
|
||||
// JSON 자체가 없어서 템플릿도 없는 경우 (최후 fallback)
|
||||
switch (CurrentLanguage)
|
||||
{
|
||||
case Language.Korean:
|
||||
return $"[KO] 해당 키를 찾을 수 없습니다: {key}";
|
||||
case Language.Japanese:
|
||||
return $"[JP] キーが見つかりません: {key}";
|
||||
default:
|
||||
return $"[EN] Could not find key: {key}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static string GetLanguageFolderPath()
|
||||
{
|
||||
string scriptDir = "Assets\\@Eden_Tools\\Eden_AutoMorpher\\LanguageManager\\Language";
|
||||
#if UNITY_EDITOR
|
||||
// AutoMorpherLanguage 스크립트의 GUID 찾기
|
||||
string[] guids = AssetDatabase.FindAssets("LanguageManager t:MonoScript");
|
||||
if (guids == null || guids.Length == 0)
|
||||
{
|
||||
Debug.LogError("[LanguageManager] Could not find LanguageManager.cs script.");
|
||||
return "Assets"; // 최소 fallback
|
||||
}
|
||||
|
||||
string scriptPath = AssetDatabase.GUIDToAssetPath(guids[0]); // 예: Assets/@Eden_Tools/.../AutoMorpherLanguage.cs
|
||||
scriptDir = Path.GetDirectoryName(scriptPath); // 그 폴더
|
||||
|
||||
|
||||
// 폴더가 없으면 에디터에서 자동 생성
|
||||
if (!Directory.Exists(scriptDir))
|
||||
{
|
||||
Directory.CreateDirectory(scriptDir);
|
||||
AssetDatabase.Refresh();
|
||||
}
|
||||
#endif
|
||||
// Unity Asset 경로 포맷으로 정리
|
||||
scriptDir = scriptDir.Replace("\\", "/");
|
||||
return scriptDir;
|
||||
}
|
||||
|
||||
public static string GetLanguageJsonAssetPath(string fileNameWithoutExt)
|
||||
{
|
||||
string folder = GetLanguageFolderPath();
|
||||
string path = Path.Combine(folder, fileNameWithoutExt + ".json");
|
||||
return path.Replace("\\", "/");
|
||||
}
|
||||
|
||||
public static string GetFormat(string key, params object[] args)
|
||||
{
|
||||
string raw = Get(key);
|
||||
if (string.IsNullOrEmpty(raw))
|
||||
return string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
return string.Format(raw, args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"[LanguageManager] Format failed for key: {key}\n{ex}");
|
||||
return raw; // 최소한 원문이라도 보여주기
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user