From 8e1adbd90774a3698ca6f1dd4cb4905acc7de3dc Mon Sep 17 00:00:00 2001 From: tymmkang Date: Sun, 1 Feb 2026 14:24:15 +0900 Subject: [PATCH] =?UTF-8?q?LanguageManager=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/@Eden_Tools.meta | 8 + Assets/@Eden_Tools/Eden_AutoMorpher.meta | 8 + .../Eden_AutoMorpher/LanguageManager.meta | 8 + .../LanguageManager/Language.meta | 8 + .../LanguageManager/Language/Language_en.json | 213 +++++++++++++ .../Language/Language_en.json.meta | 7 + .../LanguageManager/Language/Language_ja.json | 212 +++++++++++++ .../Language/Language_ja.json.meta | 7 + .../LanguageManager/Language/Language_ko.json | 211 +++++++++++++ .../Language/Language_ko.json.meta | 7 + .../LanguageManager/LanguageManager.cs | 285 ++++++++++++++++++ .../LanguageManager/LanguageManager.cs.meta | 2 + 12 files changed, 976 insertions(+) create mode 100644 Assets/@Eden_Tools.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_en.json create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_en.json.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ja.json create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ja.json.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ko.json create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ko.json.meta create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/LanguageManager.cs create mode 100644 Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/LanguageManager.cs.meta diff --git a/Assets/@Eden_Tools.meta b/Assets/@Eden_Tools.meta new file mode 100644 index 0000000..5b7f619 --- /dev/null +++ b/Assets/@Eden_Tools.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dcaa6555c32099948b000b2924ed7e2f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher.meta b/Assets/@Eden_Tools/Eden_AutoMorpher.meta new file mode 100644 index 0000000..4bd1cc8 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4826438966086d644a3997c5803cf5aa +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager.meta new file mode 100644 index 0000000..c5fa2fa --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 839d259bdc2a4ec45b739da7ecbf4e70 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language.meta new file mode 100644 index 0000000..c98d54e --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f3c01fc12e499944da78f3441a00f36a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_en.json b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_en.json new file mode 100644 index 0000000..00d2497 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_en.json @@ -0,0 +1,213 @@ +{ + "entries": [ + { + "key": "UI.Mode.AutoFitting", + "value": "Auto Fitting Mode" + }, + { + "key": "UI.Mode.ManualFitting", + "value": "Manual Fitting Mode" + }, + { + "key": "UI.Mode.ProfileFitting", + "value": "Profile Mode" + }, + { + "key": "Error.BodyMesh.NotFound", + "value": "Failed to find Body Mesh." + }, + { + "key": "Error.KeyNotFound", + "value": "[EN] Could not find key: {0}" + }, + { + "key": "UI.Mode.ManualFittingGuide", + "value": "Manual Setting Mode\n1. Assign the Objects and Meshes for the Source / Target Avatars.\n2. Adjust the target clothes object (Target Clothes Object) to match the target avatar.\n3. Press [Run Fitting] to start the fitting.\n4. Press [Run Weighting] to start weight assignment." + }, + { + "key": "UI.Mode.AutoFittingGuide", + "value": "Auto Setting Mode\n1. Assign the Source avatar body and clothing objects to the Source Avatar.\n2. Assign the target avatar body to the Target Avatar.\n3. Press [Run Fitting] to begin fitting.\n4. Press [Run Weighting] to start weight assignment." + }, + { + "key": "UI.Property.MinMarginInfo", + "value": "If the clothes stick too closely to the body, increase the MinMargin value. (Default: 0.003)" + }, + { + "key": "UI.Property.SkipFootFittingInfo", + "value": "Check this option to disable foot-based shape corrections during the fitting stage." + }, + { + "key": "UI.Property.BodyMeshTitle", + "value": "Automatically Assign Body Mesh" + }, + { + "key": "UI.Property.BodyMeshInfo", + "value": "If the body mesh cannot be detected automatically, \nplease manually assign the mesh corresponding to the body (excluding the head) to Body Meshes." + }, + { + "key": "UI.Property.SigmaInfo", + "value": "Increasing this value makes a wider area of the clothing deform smoothly together. (Default: 0.85)" + }, + { + "key": "UI.Property.SmoothingInfo", + "value": "Increasing this value applies the Smooth effect more strongly, making the deformation smoother. (Default: 1)\n - Higher values will increase computation time." + }, + { + "key": "UI.Property.IterationInfo", + "value": "If you want more deformation for the clothes, increase the number of fitting iterations." + }, + { + "key": "UI.Property.ReparentAccessoryBones", + "value": "Moves and places accessory bones into the target avatar’s armature during weighting." + }, + { + "key": "UI.Validator.ReferenceCheck", + "value": "The following items must all be assigned:" + }, + { + "key": "UI.Validator.SourceClothesChildCheck", + "value": "The Source Clothes Object must be a child of the Source Avatar Object.\n→ Please place the clothes under the Source Avatar object." + }, + { + "key": "UI.Validator.TargetClothesChildCheck", + "value": "The Target Clothes Object must be a child of the Target Avatar Object.\n→ Please place the clothes under the Target Avatar object." + }, + { + "key": "UI.Validator.ClothesArmatureCheck", + "value": "Clothes Object - The Bone Armature referenced by the SkinnedMeshRenderer is not correctly included in the Clothes Object." + }, + { + "key": "UI.Validator.SourceAvatarAnimatorCheck", + "value": "The Source Avatar Object does not have a Humanoid Avatar assigned.\n→ Please check the Animator component and Avatar settings." + }, + { + "key": "UI.Validator.TargetAvatarAnimatorCheck", + "value": "The Target Avatar Object does not have a Humanoid Avatar assigned.\n→ Please check the Animator component and Avatar settings." + }, + { + "key": "UI.Validator.CheckRendererCountCheck", + "value": "The number of SkinnedMeshRenderers in the Source / Target Clothes does not match." + }, + { + "key": "UI.Validator.VertexCountCheck", + "value": "The vertex count of the corresponding SkinnedMeshRenderer meshes does not match." + }, + { + "key": "UI.Validator.Can'tFitting", + "value": "Unable to run\n\n" + }, + { + "key": "UI.Exception.title.BodyProxyNull", + "value": "Body Proxy Null Error" + }, + { + "key": "UI.Exception.message.BodyProxyNull", + "value": "Failed to assign the Body Proxy.\nPlease make sure the Source Avatar is correctly assigned and the bones are properly set in the Animator." + }, + + { + "key": "UI.Exception.title.SourceBodyMeshNull", + "value": "Source Body Mesh Not Found" + }, + { + "key": "UI.Exception.message.SourceBodyMeshNull", + "value": "Could not find the Source Body Mesh." + }, + + { + "key": "UI.Exception.title.TargetBodyMeshNull", + "value": "Target Body Mesh Not Found" + }, + { + "key": "UI.Exception.message.TargetBodyMeshNull", + "value": "Could not find the Target Body Mesh." + }, + { + "key": "UI.Exception.title.BodyMeshNull", + "value": "BodyMesh Search Failed" + }, + { + "key": "UI.Exception.message.BodyMeshNull", + "value": "The BodyMesh of [{0}] could not be found." + }, + { + "key": "UI.Exception.title.TargetRendererNull", + "value": "Target Renderer Null Error" + }, + { + "key": "UI.Exception.message.TargetRendererNull", + "value": "targetRenderers contains one or more null elements." + }, + + { + "key": "UI.Exception.title.ClothInstanceAllocateFail", + "value": "ClothInstance Allocation Failed" + }, + { + "key": "UI.Exception.message.ClothInstanceAllocateFail", + "value": "No ClothInstance could be allocated.\nPlease check if the clothes have a SkinnedMeshRenderer." + }, + + { + "key": "UI.Exception.title.WeightingNotReady", + "value": "Weighting Is Not Ready" + }, + { + "key": "UI.Exception.message.WeightingNotReady", + "value": "Fitting has not finished before calling WeightingEnumerator, or the Target Avatar has been changed.\nPlease run Run Fitting again." + }, + + { + "key": "UI.Exception.title.BodyBVHFail", + "value": "Body BVH Creation Failed" + }, + { + "key": "UI.Exception.message.BodyBVHFail", + "value": "Failed to build BVH for {0}.\nbvhMesh is Null : {1}\nbvhMesh triangles is Null : {2}" + }, + { + "key": "UI.ProfileSaver.Guide", + "value": "This section is for generating Profile data.\nPlease set the avatar Scale to 1,1,1.\nAdjust ShapeKeys so that the avatar BodyMesh is not occluded." + }, + { + "key": "UI.ProfileSaver.ProfileName.Tooltip", + "value": "Name of the Profile to be saved" + }, + { + "key": "UI.ProfileSaver.SourceAvatar.Tooltip", + "value": "Avatar root Transform with Humanoid Rig and Animator configured" + }, + { + "key": "UI.ProfileSaver.SourceBodyMeshes.Tooltip", + "value": "List of body SkinnedMeshRenderers used for Profile generation" + }, + { + "key": "UI.ProfileSaver.Error.ProfileNameEmpty", + "value": "Profile Name is empty." + }, + { + "key": "UI.ProfileSaver.Error.SourceAvatarNull", + "value": "Source Avatar is not assigned." + }, + { + "key": "UI.ProfileSaver.Error.SourceAvatarAnimatorMissing", + "value": "Source Avatar does not have an Animator." + }, + { + "key": "UI.ProfileSaver.Error.SourceBodyMeshesEmpty", + "value": "Source Body Meshes is empty." + }, + { + "key": "UI.ProfileSaver.Error.SourceBodyMeshesContainNull", + "value": "Source Body Meshes contains NULL." + }, + { + "key": "UI.ProfileSaver.Error.BodyMeshNotChildOfAvatar", + "value": "Source Body Meshes must be children of the Source Avatar." + }, + { + "key": "UI.ProfileSaver.Error.BaseDataMissing", + "value": "No Base file exists in the BaseData folder." + } + ] +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_en.json.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_en.json.meta new file mode 100644 index 0000000..7d48f66 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_en.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0aa9f12300a0596408771775b2d5148e +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ja.json b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ja.json new file mode 100644 index 0000000..3fc0e89 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ja.json @@ -0,0 +1,212 @@ +{ + "entries": [ + { + "key": "UI.Mode.AutoFitting", + "value": "自動セットアップモード" + }, + { + "key": "UI.Mode.ManualFitting", + "value": "手動セットアップモード" + }, + { + "key": "UI.Mode.ProfileFitting", + "value": "プロファイルモード" + }, + { + "key": "Error.BodyMesh.NotFound", + "value": "Body Mesh を見つけられませんでした。" + }, + { + "key": "Error.KeyNotFound", + "value": "[JP] キーが見つかりません: {0}" + }, + { + "key": "UI.Mode.ManualFittingGuide", + "value": "手動セットアップモード\n1. Source / Target Avatar に対応するオブジェクトとメッシュを割り当ててください。\n2. 対応する衣装(Target Clothes Object)をターゲットアバターに合わせて調整してください。\n3. [Run Fitting] を押してフィッティングを開始してください。\n4. [Run Weighting] を押してウェイト割り当てを開始してください。" + }, + { + "key": "UI.Mode.AutoFittingGuide", + "value": "自動セットアップモード\n1. Source Avatar にソースアバターのボディと衣装オブジェクトを割り当ててください。\n2. Target Avatar にターゲットアバターのボディを割り当ててください。\n3. [Run Fitting] を押してフィッティングを開始してください。\n4. [Run Weighting] を押してウェイト割り当てを開始してください。" + }, + { + "key": "UI.Property.MinMarginInfo", + "value": "衣装が体に密着しすぎる場合は、MinMarginの値を大きくしてください。(初期値: 0.003)" + }, + { + "key": "UI.Property.SkipFootFittingInfo", + "value": "Fitting工程において、足(foot)を基準とした形状補正を適用しない場合はチェックしてください。" + }, + { + "key": "UI.Property.BodyMeshTitle", + "value": "ボディメッシュを自動で割り当てる" + }, + { + "key": "UI.Property.BodyMeshInfo", + "value": "ボディメッシュを自動で検出できない場合は、\n(頭部を除く)ボディに該当するメッシュを Body Meshes に手動で割り当ててください。" + }, + { + "key": "UI.Property.SigmaInfo", + "value": "値を上げるほど、衣装のより広い範囲が一緒に滑らかに変形します。(初期値:0.85)" + }, + { + "key": "UI.Property.SmoothingInfo", + "value": "値を上げるほど、Smooth効果がより強く適用され、変形がより滑らかになります。(初期値:1)\n - 値を上げると処理時間が長くなります。" + }, + { + "key": "UI.Property.IterationInfo", + "value": "衣装をさらに変形させたい場合は、Fittingの反復回数を増やしてください。" + }, + { + "key": "UI.Property.ReparentAccessoryBones", + "value": "Weighting 時にアクセサリーボーンを Target アバターのアーマチュアへ移動して配置します。" + }, + { + "key": "UI.Validator.ReferenceCheck", + "value": "次の項目がすべて割り当てられている必要があります:" + }, + { + "key": "UI.Validator.SourceClothesChildCheck", + "value": "Source Clothes Object は必ず Source Avatar Object の子オブジェクトである必要があります。\n→ Source Avatar オブジェクトの下に衣装を配置してください。" + }, + { + "key": "UI.Validator.TargetClothesChildCheck", + "value": "Target Clothes Object は必ず Target Avatar Object の子オブジェクトである必要があります。\n→ Target Avatar オブジェクトの下に衣装を配置してください。" + }, + { + "key": "UI.Validator.ClothesArmatureCheck", + "value": "Clothes Object - Clothes オブジェクトに、SkinnedMeshRenderer が参照するボーンアーマチュアが正しく含まれていません。" + }, + { + "key": "UI.Validator.SourceAvatarAnimatorCheck", + "value": "Source Avatar Object に Humanoid Avatar が設定されていません。\n→ Animator コンポーネントと Avatar 設定を確認してください。" + }, + { + "key": "UI.Validator.TargetAvatarAnimatorCheck", + "value": "Target Avatar Object に Humanoid Avatar が設定されていません。\n→ Animator コンポーネントと Avatar 設定を確認してください。" + }, + { + "key": "UI.Validator.CheckRendererCountCheck", + "value": "Source / Target Clothes の SkinnedMeshRenderer の数が一致していません。" + }, + { + "key": "UI.Validator.VertexCountCheck", + "value": "対応する SkinnedMeshRenderer の Mesh vertex 数が一致していません。" + }, + { + "key": "UI.Validator.Can'tFitting", + "value": "を実行できません\n\n" + }, + { + "key": "UI.Exception.title.BodyProxyNull", + "value": "Body Proxy Null エラー" + }, + { + "key": "UI.Exception.message.BodyProxyNull", + "value": "Body Proxy を割り当てることができませんでした。\nSource Avatar が正しく割り当てられているか、Animator にボーンが正しく設定されているか確認してください。" + }, + + { + "key": "UI.Exception.title.SourceBodyMeshNull", + "value": "Source Body Mesh の探索に失敗しました" + }, + { + "key": "UI.Exception.message.SourceBodyMeshNull", + "value": "Source Body Mesh を見つけることができませんでした。" + }, + + { + "key": "UI.Exception.title.TargetBodyMeshNull", + "value": "Target Body Mesh の探索に失敗しました" + }, + { + "key": "UI.Exception.message.TargetBodyMeshNull", + "value": "Target Body Mesh を見つけることができませんでした。" + }, + { + "key": "UI.Exception.title.BodyMeshNull", + "value": "BodyMesh の探索に失敗しました" + }, + { + "key": "UI.Exception.message.BodyMeshNull", + "value": "[{0}] の BodyMesh を見つけることができません。" + }, + { + "key": "UI.Exception.title.TargetRendererNull", + "value": "Target Renderer Null エラー" + }, + { + "key": "UI.Exception.message.TargetRendererNull", + "value": "targetRenderers に null が含まれています。" + }, + + { + "key": "UI.Exception.title.ClothInstanceAllocateFail", + "value": "ClothInstance の割り当てに失敗しました" + }, + { + "key": "UI.Exception.message.ClothInstanceAllocateFail", + "value": "割り当て可能な ClothInstance がありません。\n衣装に SkinnedMeshRenderer が含まれているか確認してください。" + }, + + { + "key": "UI.Exception.title.WeightingNotReady", + "value": "Weighting の準備ができていません" + }, + { + "key": "UI.Exception.message.WeightingNotReady", + "value": "WeightingEnumerator を呼び出す前に Fitting が完了していないか、Target Avatar が変更されています。\nRun Fitting を再実行してください。" + }, + { + "key": "UI.Exception.title.BodyBVHFail", + "value": "Body BVH の生成に失敗しました" + }, + { + "key": "UI.Exception.message.BodyBVHFail", + "value": "{0} の BVH の生成に失敗しました。\nbvhMesh is Null : {1}\nbvhMesh triangles is Null : {2}" + }, + { + "key": "UI.ProfileSaver.Guide", + "value": "Profileデータ生成のための設定です。\nアバターのScaleは 1,1,1 に設定してください。\nアバターのBodyMeshが隠れないようにShapeKeyを調整してください。" + }, + { + "key": "UI.ProfileSaver.ProfileName.Tooltip", + "value": "保存されるProfileの名前" + }, + { + "key": "UI.ProfileSaver.SourceAvatar.Tooltip", + "value": "Humanoid Rig と Animator が設定された Avatar の Root Transform" + }, + { + "key": "UI.ProfileSaver.SourceBodyMeshes.Tooltip", + "value": "Profile生成に使用される Body SkinnedMeshRenderer の一覧" + }, + { + "key": "UI.ProfileSaver.Error.ProfileNameEmpty", + "value": "Profile Name が空です。" + }, + { + "key": "UI.ProfileSaver.Error.SourceAvatarNull", + "value": "Source Avatar が指定されていません。" + }, + { + "key": "UI.ProfileSaver.Error.SourceAvatarAnimatorMissing", + "value": "Source Avatar に Animator が存在しません。" + }, + { + "key": "UI.ProfileSaver.Error.SourceBodyMeshesEmpty", + "value": "Source Body Meshes が空です。" + }, + { + "key": "UI.ProfileSaver.Error.SourceBodyMeshesContainNull", + "value": "Source Body Meshes に NULL が含まれています。" + }, + { + "key": "UI.ProfileSaver.Error.BodyMeshNotChildOfAvatar", + "value": "Source Body Meshes は必ず Source Avatar の子オブジェクトである必要があります。" + }, + { + "key": "UI.ProfileSaver.Error.BaseDataMissing", + "value": "BaseData フォルダに Base ファイルが存在しません。" + } + ] +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ja.json.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ja.json.meta new file mode 100644 index 0000000..7fe9b9b --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ja.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 07befa424eefac04e8670c57d4c2a2ec +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ko.json b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ko.json new file mode 100644 index 0000000..3508022 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ko.json @@ -0,0 +1,211 @@ +{ + "entries": [ + { + "key": "UI.Mode.AutoFitting", + "value": "자동 셋팅 모드" + }, + { + "key": "UI.Mode.ManualFitting", + "value": "직접 셋팅 모드" + }, + { + "key": "UI.Mode.ProfileFitting", + "value": "프로필 모드" + }, + { + "key": "Error.BodyMesh.NotFound", + "value": "Body Mesh를 찾지 못했습니다." + }, + { + "key": "Error.KeyNotFound", + "value": "[KO] 해당 키를 찾을 수 없습니다: {0}" + }, + { + "key": "UI.Mode.ManualFittingGuide", + "value": "직접 셋팅 모드\n1. Source / Target Avatar에 해당하는 Object와 Mesh들을 할당해 주세요.\n2. 대응할 의상(Target Clothes Object)을 대응할 아바타에 맞춰 조정해주세요.\n3. [Run Fitting]을 눌러서 대응을 시작하세요.\n4. [Run Weighting]을 눌러서 Weight 할당을 시작하세요." + }, + { + "key": "UI.Mode.AutoFittingGuide", + "value": "자동 셋팅 모드\n1. Source Avatar에 Source 아바타 바디와 의상 Object를 할당해주세요.\n2. Target Avatar에 Target 아바타 바디를 할당해주세요.\n3. [Run Fitting]을 눌러 대응을 시작하세요.\n4. [Run Weighting]을 눌러서 Weight 할당을 시작하세요." + }, + { + "key": "UI.Property.MinMarginInfo", + "value": "의상이 몸에 너무 달라 붙는다면, MinMargin 값을 늘려주세요. (초기값: 0.003)" + }, + { + "key": "UI.Property.SkipFootFittingInfo", + "value": "Fitting 단계에서 발(foot) 기준의 형태 보정을 적용하지 않으려면 체크해주세요." + }, + { + "key": "UI.Property.BodyMeshTitle", + "value": "Body Mesh 자동으로 할당하기." + }, + { + "key": "UI.Property.BodyMeshInfo", + "value": "Body Mesh를 자동으로 찾지 못 할 경우, \n(Head 제외) 바디에 해당하는 Mesh를 Body Meshes에 할당해 주세요." + }, + { + "key": "UI.Property.SigmaInfo", + "value": "값을 높일수록 의상의 더 넓은 부분이 함께 부드럽게 변형됩니다. (초기값: 0.85)" + }, + { + "key": "UI.Property.SmoothingInfo", + "value": "값을 높일수록 Smooth 효과가 더 많이 적용되어 변형이 더욱 부드러워집니다. (초기값: 1)\n - 값이 높아질수록 연산 시간이 증가합니다." + }, + { + "key": "UI.Property.IterationInfo", + "value": "의상을 더 많이 변형시키고 싶다면, Fitting 반복 횟수를 늘려주세요." + }, + { + "key": "UI.Property.ReparentAccessoryBones", + "value": "Weighting시 악세서리 본을 Target Avatar에 Armature로 옮겨 배치합니다." + }, + { + "key": "UI.Validator.ReferenceCheck", + "value": "다음 항목이 모두 할당되어야 합니다:" + }, + { + "key": "UI.Validator.SourceClothesChildCheck", + "value": "Source Clothes Object 는 반드시 Source Avatar Object 의 자식이어야 합니다.\n→ Source Avatar 오브젝트 하위에 의상을 넣어 주세요." + }, + { + "key": "UI.Validator.TargetClothesChildCheck", + "value": "Target Clothes Object 는 반드시 Target Avatar Object 의 자식이어야 합니다.\n→ Target Avatar 오브젝트 하위에 의상을 넣어 주세요." + }, + { + "key": "UI.Validator.ClothesArmatureCheck", + "value": "Clothes Object - 의상에 SkinnedMeshRenderer가 참조하는 Bone Armature가 올바르게 포함되어 있지 않습니다." + }, + { + "key": "UI.Validator.SourceAvatarAnimatorCheck", + "value": "Source Avatar Object 에 Humanoid Avatar 가 설정되어 있지 않습니다.\n→ Animator 컴포넌트 및 Avatar 설정을 확인해 주세요." + }, + { + "key": "UI.Validator.TargetAvatarAnimatorCheck", + "value": "Target Avatar Object 에 Humanoid Avatar 가 설정되어 있지 않습니다.\n→ Animator 컴포넌트 및 Avatar 설정을 확인해 주세요." + }, + { + "key": "UI.Validator.CheckRendererCountCheck", + "value": "Source / Target Clothes 의 SkinnedMeshRenderer 개수가 일치하지 않습니다." + }, + { + "key": "UI.Validator.VertexCountCheck", + "value": "대응되는 SkinnedMeshRenderer 의 Mesh vertex 수가 일치하지 않습니다." + }, + { + "key": "UI.Validator.Can'tFitting", + "value": "실행 불가\n\n" + }, + { + "key": "UI.Exception.title.BodyProxyNull", + "value": "Body Proxy Null 에러" + }, + { + "key": "UI.Exception.message.BodyProxyNull", + "value": "Body Proxy를 할당할 수 없습니다.\nSource Avatar가 제대로 할당되어 있으며, Animator에 본이 제대로 셋팅되어 있는지 확인해주세요." + }, + { + "key": "UI.Exception.title.SourceBodyMeshNull", + "value": "Source Body Mesh 탐색 실패" + }, + { + "key": "UI.Exception.message.SourceBodyMeshNull", + "value": "Source Body Mesh 를 찾을 수 없습니다." + }, + { + "key": "UI.Exception.title.TargetBodyMeshNull", + "value": "Target Body Mesh 탐색 실패" + }, + { + "key": "UI.Exception.message.TargetBodyMeshNull", + "value": "Target Body Mesh 를 찾을 수 없습니다." + }, + { + "key": "UI.Exception.title.BodyMeshNull", + "value": "Body Mesh 탐색 실패" + }, + { + "key": "UI.Exception.message.BodyMeshNull", + "value": "[{0}]의 Body Mesh 를 찾을 수 없습니다." + }, + + { + "key": "UI.Exception.title.TargetRendererNull", + "value": "Target Renderer Null 에러" + }, + { + "key": "UI.Exception.message.TargetRendererNull", + "value": "targetRenderers 에 null 이 포함되어 있습니다." + }, + + { + "key": "UI.Exception.title.ClothInstanceAllocateFail", + "value": "ClothInstance 할당 실패" + }, + { + "key": "UI.Exception.message.ClothInstanceAllocateFail", + "value": "할당할 수 있는 ClothInstance가 없습니다.\n의상에 SkinnedMeshRenderer가 있는지 확인해주세요." + }, + + { + "key": "UI.Exception.title.WeightingNotReady", + "value": "Weighting이 준비되지 않았습니다." + }, + { + "key": "UI.Exception.message.WeightingNotReady", + "value": "WeightingEnumerator 호출 전 Fitting이 완료되지 않았거나, Target Avatar가 변경되었습니다.\nRun Fitting을 다시 실행해 주세요." + }, + { + "key": "UI.Exception.title.BodyBVHFail", + "value": "Body BVH 생성 실패" + }, + { + "key": "UI.Exception.message.BodyBVHFail", + "value": "{0} 의 BVH를 만드는데 실패하였습니다.\nbvhMesh is Null : {1}\nbvhMesh triangles is Null : {2}" + }, + { + "key": "UI.ProfileSaver.Guide", + "value": "Profile 데이터 생성을 위한 설정 영역입니다.\n아바타의 Scale은 1,1,1로 맞춰주세요.\n아바타의 BodyMesh가 가려지지 않도록 ShapeKey를 조절해 주세요." + }, + { + "key": "UI.ProfileSaver.ProfileName.Tooltip", + "value": "저장될 Profile 이름" + }, + { + "key": "UI.ProfileSaver.SourceAvatar.Tooltip", + "value": "Humanoid Rig + Animator가 설정된 Avatar Root Transform" + }, + { + "key": "UI.ProfileSaver.SourceBodyMeshes.Tooltip", + "value": "Profile 생성에 사용될 Body SkinnedMeshRenderer 목록" + }, + { + "key": "UI.ProfileSaver.Error.ProfileNameEmpty", + "value": "Profile Name이 비어 있습니다." + }, + { + "key": "UI.ProfileSaver.Error.SourceAvatarNull", + "value": "Source Avatar가 지정되지 않았습니다." + }, + { + "key": "UI.ProfileSaver.Error.SourceAvatarAnimatorMissing", + "value": "Source Avatar에 Animator가 존재하지 않습니다." + }, + { + "key": "UI.ProfileSaver.Error.SourceBodyMeshesEmpty", + "value": "Source Body Meshes가 비어 있습니다." + }, + { + "key": "UI.ProfileSaver.Error.SourceBodyMeshesContainNull", + "value": "Source Body Meshes에 NULL이 존재합니다." + }, + { + "key": "UI.ProfileSaver.Error.BodyMeshNotChildOfAvatar", + "value": "Source Body Meshes 는 반드시 Source Avatar 의 자식이어야 합니다." + }, + { + "key": "UI.ProfileSaver.Error.BaseDataMissing", + "value": "BaseData 폴더에 Base 파일이 존재하지 않습니다." + } + ] +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ko.json.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ko.json.meta new file mode 100644 index 0000000..35bc68d --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/Language/Language_ko.json.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 06a08864e1cbb6740a46cbde54883c6a +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/LanguageManager.cs b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/LanguageManager.cs new file mode 100644 index 0000000..2b1fe66 --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/LanguageManager.cs @@ -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 _koTable; + private static Dictionary _jaTable; + private static Dictionary _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); + } + + /// + /// 현재 언어에서 문자열 가져오기. + /// key 없으면 fallback → 다른 언어 → 최종적으로 key 자체 반환. + /// + public static string Get(string key) + { + if (string.IsNullOrEmpty(key)) + return string.Empty; + + // 현재 언어 테이블에서 먼저 검색 + if (TryGetFromCurrentLanguage(key, out string value)) + return value; + + return FormatKeyNotFound(key); + } + + /// + /// 언어별로 다른 키를 쓰고 싶을 때 (번호 기반 등)도 가능하도록 + /// 번호를 바로 key 문자열로 변환해서 사용하는 버전. + /// + 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 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 LoadTable(string fileName) + { + var dict = new Dictionary(); + +#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(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; // 최소한 원문이라도 보여주기 + } + } + + } +} diff --git a/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/LanguageManager.cs.meta b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/LanguageManager.cs.meta new file mode 100644 index 0000000..e76a6cb --- /dev/null +++ b/Assets/@Eden_Tools/Eden_AutoMorpher/LanguageManager/LanguageManager.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: b4990eee90808bc488656e484f9ea0d8 \ No newline at end of file