diff --git a/Assets/_DDD/_Scripts/GameFramework/Localization/GoogleSheetData.cs b/Assets/_DDD/_Scripts/GameFramework/Localization/GoogleSheetData.cs new file mode 100644 index 000000000..ba1008493 --- /dev/null +++ b/Assets/_DDD/_Scripts/GameFramework/Localization/GoogleSheetData.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; + +namespace DDD +{ + [Serializable] + public class SheetTableData + { + public string Key; + public Dictionary LocaleValues = new(); // ex: "en" → "Potion" + } + + [Serializable] + public class SheetUploadPayload + { + public Dictionary> Tables = new(); // TableName → Entries + } + + [Serializable] + public class SheetDownloadPayload + { + public Dictionary>> Tables = new(); // TableName → Rows (Key, en, ko, ja ...) + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/GameFramework/Localization/GoogleSheetWebClient.cs b/Assets/_DDD/_Scripts/GameFramework/Localization/GoogleSheetWebClient.cs new file mode 100644 index 000000000..b52744397 --- /dev/null +++ b/Assets/_DDD/_Scripts/GameFramework/Localization/GoogleSheetWebClient.cs @@ -0,0 +1,48 @@ +using UnityEngine; +using UnityEngine.Networking; +using System.Text; +using System.Threading.Tasks; + +namespace DDD +{ + public static class GoogleSheetWebClient + { + public static async Task Get(string url) + { + var req = UnityWebRequest.Get(url); + var op = req.SendWebRequest(); + + while (!op.isDone) + await Task.Yield(); + + if (req.result != UnityWebRequest.Result.Success) + { + Debug.LogError("GET Failed: " + req.error); + return null; + } + + return req.downloadHandler.text; + } + + public static async Task Post(string url, string json) + { + var req = new UnityWebRequest(url, "POST"); + byte[] bodyRaw = Encoding.UTF8.GetBytes(json); + req.uploadHandler = new UploadHandlerRaw(bodyRaw); + req.downloadHandler = new DownloadHandlerBuffer(); + req.SetRequestHeader("Content-Type", "application/json"); + + var op = req.SendWebRequest(); + while (!op.isDone) + await Task.Yield(); + + if (req.result != UnityWebRequest.Result.Success) + { + Debug.LogError("POST Failed: " + req.error); + return null; + } + + return req.downloadHandler.text; + } + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationExporter.cs b/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationExporter.cs new file mode 100644 index 000000000..ba5b0d6f0 --- /dev/null +++ b/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationExporter.cs @@ -0,0 +1,66 @@ +#if UNITY_EDITOR +using UnityEngine; +using System.Collections.Generic; +using Newtonsoft.Json; +using UnityEditor.Localization; +using UnityEngine.Localization.Settings; +using UnityEngine.Localization.Tables; + +namespace DDD +{ + public static class LocalizationExporter + { + public static async void ExportAllToSheet(string webAppUrl) + { + var collections = LocalizationEditorSettings.GetStringTableCollections(); + var exportPayload = new SheetUploadPayload(); + + List localeOrder = GetOrderedLocaleCodes(); + + foreach (var collection in collections) + { + string tableName = collection.TableCollectionName; + var shared = collection.SharedData; + var entries = new List(); + + foreach (var sharedEntry in shared.Entries) + { + var row = new SheetTableData { Key = sharedEntry.Key }; + foreach (var localeCode in localeOrder) + { + var table = collection.GetTable(localeCode) as StringTable; + if (table != null) + { + string value = table.GetEntry(sharedEntry.Id)?.Value ?? ""; + row.LocaleValues[localeCode] = value; + } + } + entries.Add(row); + } + + exportPayload.Tables[tableName] = entries; + } + + string json = JsonConvert.SerializeObject(exportPayload); + Debug.Log($"Localization Exporting..."); + + var response = await GoogleSheetWebClient.Post(webAppUrl, json); + Debug.Log($"[Localization Export 완료] 응답: {response}"); + } + + /// + /// Localization Settings에 등록된 Locale 순서대로 Locale 코드 리스트 반환 + /// + public static List GetOrderedLocaleCodes() + { + var locales = LocalizationSettings.AvailableLocales.Locales; + var result = new List(); + foreach (var locale in locales) + { + result.Add(locale.Identifier.Code); // 예: "ko", "en", "ja" + } + return result; + } + } +} +#endif \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationImporter.cs b/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationImporter.cs new file mode 100644 index 000000000..9d8a212da --- /dev/null +++ b/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationImporter.cs @@ -0,0 +1,56 @@ +#if UNITY_EDITOR +using UnityEngine; +using UnityEditor; +using UnityEngine.Localization.Tables; +using System.Collections.Generic; +using Newtonsoft.Json; +using UnityEditor.Localization; + +namespace DDD +{ + public static class LocalizationImporter + { + public static async void ImportAllFromSheet(string webAppUrl) + { + string json = await GoogleSheetWebClient.Get(webAppUrl); + if (string.IsNullOrEmpty(json)) return; + + var payload = JsonConvert.DeserializeObject(json); + foreach (var kv in payload.Tables) + { + string tableName = kv.Key; + List> rows = kv.Value; + + var collection = LocalizationEditorSettings.GetStringTableCollection(tableName); + if (collection == null) + { + Debug.LogWarning($"[Localization] Table not found: {tableName}"); + continue; + } + + var shared = collection.SharedData; + + foreach (var row in rows) + { + if (!row.TryGetValue("Key", out var key)) continue; + var sharedEntry = shared.GetEntry(key) ?? shared.AddKey(key); + + foreach (var localeCode in row.Keys) + { + if (localeCode == "Key") continue; + var table = collection.GetTable(localeCode) as StringTable; + if (table == null) continue; + + table.AddEntry(sharedEntry.Id, row[localeCode]); + EditorUtility.SetDirty(table); + } + } + + AssetDatabase.SaveAssets(); + } + + Debug.Log("[Localization Import] 완료"); + } + } +} +#endif \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationManager.cs b/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationManager.cs index 4decd28fb..eccd25c8d 100644 --- a/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationManager.cs +++ b/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationManager.cs @@ -1,31 +1,56 @@ -#if UNITY_EDITOR -using UnityEditor; -using UnityEngine; +using UnityEngine.Localization.Settings; +using System.Linq; namespace DDD { - public class LocalizationManager : MonoBehaviour + public enum TableName { - [SerializeField] - private string _webUrl = "https://script.google.com/macros/s/AKfycbx6ZrQNJIot1A_uwjilgOxHEm7zPommHyi7nH-x1d8sNgMEUeoqHf8iwvEnA1AxKBU6/exec"; - - // [MenuItem("Tools/Localization/Import All From Sheet")] - // public static void ImportAll() - // { - // var url = GetManager()?.WebUrl; - // if (string.IsNullOrEmpty(url)) return; - // - // LocalizationImporter.ImportAll(url); - // } - // - // [MenuItem("Tools/Localization/Export All To Sheet")] - // public static void ExportAll() - // { - // var url = GetManager()?.WebUrl; - // if (string.IsNullOrEmpty(url)) return; - // - // LocalizationExporter.ExportAll(url); - // } + None = 0, + Item_Name, + Item_Description, } -} -#endif \ No newline at end of file + + public static class LocalizationManager + { + /// + /// 현재 선택된 로케일 기준으로 로컬라이징 텍스트를 가져옵니다. + /// + public static string GetString(TableName table, string key) + { + if (table == TableName.None || string.IsNullOrEmpty(key)) + return $"[Invalid:{table}/{key}]"; + + var locale = LocalizationSettings.SelectedLocale; + var tableName = table.ToString(); + + var stringTable = LocalizationSettings.StringDatabase.GetTable(tableName, locale); + if (stringTable == null) + return $"[Missing Table:{tableName}]"; + + var entry = stringTable.GetEntry(key); + if (entry == null) + return $"[Missing Key:{key}]"; + + return entry.GetLocalizedString(); + } + + /// + /// 현재 사용 중인 로케일 코드 반환 (예: "ko", "en", "ja") + /// + public static string GetCurrentLocaleCode() + { + return LocalizationSettings.SelectedLocale.Identifier.Code; + } + + /// + /// 로케일 코드로 현재 언어를 설정합니다. (예: "ko", "en", "ja") + /// + public static void SetLocale(string code) + { + var locale = LocalizationSettings.AvailableLocales.Locales + .FirstOrDefault(l => l.Identifier.Code == code); + if (locale != null) + LocalizationSettings.SelectedLocale = locale; + } + } +} \ No newline at end of file diff --git a/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationSyncTool.cs b/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationSyncTool.cs new file mode 100644 index 000000000..392f81970 --- /dev/null +++ b/Assets/_DDD/_Scripts/GameFramework/Localization/LocalizationSyncTool.cs @@ -0,0 +1,23 @@ +#if UNITY_EDITOR +using UnityEditor; + +namespace DDD +{ + public static class LocalizationSyncTool + { + public static readonly string WebAppUrl = "https://script.google.com/macros/s/AKfycbwVQ7_x0Didf_13h3qPB9lSMobBR9xr6nLffj_n8znNvfs-pdNylNA7nWzd2R-_IHGf/exec"; + + [MenuItem("Tools/Localization/Google Sheet → Unity")] + public static void ImportAll() + { + LocalizationImporter.ImportAllFromSheet(WebAppUrl); + } + + [MenuItem("Tools/Localization/Unity → Google Sheet")] + public static void ExportAll() + { + LocalizationExporter.ExportAllToSheet(WebAppUrl); + } + } +} +#endif \ No newline at end of file