Add editor localizations

This commit is contained in:
bd_ 2022-10-16 14:05:26 -07:00
parent 0bda80bca0
commit 2a2a450111
14 changed files with 330 additions and 28 deletions

View File

@ -6,6 +6,8 @@ using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.Serialization;
using static net.fushizen.modular_avatar.core.editor.Localization;
using Debug = System.Diagnostics.Debug;
namespace net.fushizen.modular_avatar.core.editor
{
@ -205,7 +207,7 @@ namespace net.fushizen.modular_avatar.core.editor
var style = new GUIStyle(EditorStyles.label);
style.fontStyle = FontStyle.Italic;
var content = new GUIContent(" Autodetected Parameters ");
var content = new GUIContent(G("params.autodetect_header"));
var size = style.CalcSize(content);
var centeredRect = new Rect(
@ -258,20 +260,21 @@ namespace net.fushizen.modular_avatar.core.editor
{
EditorGUI.PropertyField(leftHalf, nameOrPrefix, GUIContent.none);
var toggleInternalWidth = EditorStyles.toggle.CalcSize(new GUIContent("Internal")).x;
var internalGuiContent = G("params.internal");
var toggleInternalWidth = EditorStyles.toggle.CalcSize(internalGuiContent).x;
var toggleInternalRect = new Rect(rightHalfTop.x, rightHalfTop.y, toggleInternalWidth,
rightHalfTop.height);
internalParameter.boolValue =
EditorGUI.ToggleLeft(toggleInternalRect, "Internal", internalParameter.boolValue);
EditorGUI.ToggleLeft(toggleInternalRect, internalGuiContent, internalParameter.boolValue);
var isPrefixRect = new Rect(rightHalfTop.x + toggleInternalWidth + halfMargin, rightHalfTop.y,
rightHalfTop.width - toggleInternalWidth - halfMargin, rightHalfTop.height);
isPrefix.boolValue = EditorGUI.ToggleLeft(isPrefixRect, "PhysBones Prefix", isPrefix.boolValue);
isPrefix.boolValue = EditorGUI.ToggleLeft(isPrefixRect, G("params.pb_prefix"), isPrefix.boolValue);
var syncedContent = new GUIContent("Sync mode ");
var syncedContent = new GUIContent(G("params.syncmode"));
var labelSize = EditorStyles.label.CalcSize(syncedContent);
var syncedWidth = labelSize.x;
var syncedWidth = labelSize.x + EditorStyles.label.margin.right;
var syncedRect = new Rect(rightHalfSyncControlField.x, rightHalfSyncControlField.y, syncedWidth,
rightHalfSyncControlField.height);
@ -308,7 +311,7 @@ namespace net.fushizen.modular_avatar.core.editor
{
var saved = elem.FindPropertyRelative(nameof(ParameterConfig.saved));
var savedContents = new GUIContent("Saved");
var savedContents = new GUIContent(G("params.saved"));
var savedStyle = EditorStyles.toggle;
var savedSize = savedStyle.CalcSize(savedContents);
var savedLabelWidth = EditorStyles.label.CalcSize(savedContents).x;
@ -327,9 +330,9 @@ namespace net.fushizen.modular_avatar.core.editor
saved.boolValue = EditorGUI.Toggle(savedPos, saved.boolValue);
var defaultValueProp = elem.FindPropertyRelative(nameof(ParameterConfig.defaultValue));
var label = new GUIContent("Default value ");
var label = new GUIContent(G("params.default"));
var labelSize = EditorStyles.label.CalcSize(label);
var labelWidth = labelSize.x;
var labelWidth = labelSize.x + EditorStyles.label.margin.right;
var labelRect = new Rect(rightHalfDefaultValue.x, rightHalfDefaultValue.y, labelWidth,
rightHalfDefaultValue.height);
@ -403,17 +406,20 @@ namespace net.fushizen.modular_avatar.core.editor
var leftHalf = new Rect(rect.x, rect.y, rect.width / 2, rect.height);
var rightHalf = new Rect(rect.x + rect.width / 2, rect.y, rect.width / 2, rect.height);
EditorGUI.LabelField(leftHalf, "Field name");
if (!_devMode) EditorGUI.LabelField(rightHalf, "Remap to");
EditorGUI.LabelField(leftHalf, G("params.fieldname"));
if (!_devMode) EditorGUI.LabelField(rightHalf, G("params.remapto"));
}
public override void OnInspectorGUI()
{
EditorGUI.BeginChangeCheck();
_devMode = EditorGUILayout.Toggle("Developer mode", _devMode);
_devMode = EditorGUILayout.Toggle(G("params.devmode"), _devMode);
if (EditorGUI.EndChangeCheck() || _reorderableList == null || _needsRebuild) SetupList();
Debug.Assert(_reorderableList != null, nameof(_reorderableList) + " != null");
_reorderableList.DoLayoutList();
serializedObject.ApplyModifiedProperties();
Localization.ShowLanguageUI();
}
}
}

View File

@ -1,5 +1,6 @@
using UnityEditor;
using UnityEngine;
using static net.fushizen.modular_avatar.core.editor.Localization;
namespace net.fushizen.modular_avatar.core.editor
{
@ -50,7 +51,7 @@ namespace net.fushizen.modular_avatar.core.editor
var currentTarget = targets.Length != 1 ? null : ((ModularAvatarBoneProxy) targets[0]).target;
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(virtProp);
EditorGUILayout.PropertyField(virtProp, G("boneproxy.target"));
if (EditorGUI.EndChangeCheck())
{
virtObj.ApplyModifiedPropertiesWithoutUndo();
@ -62,13 +63,15 @@ namespace net.fushizen.modular_avatar.core.editor
}
}
foldout = EditorGUILayout.Foldout(foldout, "Advanced");
foldout = EditorGUILayout.Foldout(foldout, G("boneproxy.foldout.advanced"));
if (foldout)
{
EditorGUI.indentLevel++;
base.OnInspectorGUI();
EditorGUI.indentLevel--;
}
Localization.ShowLanguageUI();
}
}
}

View File

@ -4,6 +4,7 @@ using UnityEditor;
using UnityEngine;
using VRC.SDK3.Avatars.Components;
using VRC.SDK3.Avatars.ScriptableObjects;
using static net.fushizen.modular_avatar.core.editor.Localization;
namespace net.fushizen.modular_avatar.core.editor
{
@ -56,23 +57,19 @@ namespace net.fushizen.modular_avatar.core.editor
{
if (installTo.objectReferenceValue == null)
{
EditorGUILayout.HelpBox(
"Select one of your avatar's menus below to automatically install controls for this prefab."
, MessageType.Info);
EditorGUILayout.HelpBox(S("menuinstall.help.hint_set_menu"), MessageType.Info);
}
else if (!IsMenuReachable(RuntimeUtil.FindAvatarInParents(((Component) target).transform),
(VRCExpressionsMenu) installTo.objectReferenceValue))
{
EditorGUILayout.HelpBox(
"Selected menu asset is not part of your avatar."
, MessageType.Error);
EditorGUILayout.HelpBox(S("menuinstall.help.hint_bad_menu"), MessageType.Error);
}
}
EditorGUILayout.PropertyField(installTo, new GUIContent("Install To"));
EditorGUILayout.PropertyField(installTo, G("menuinstall.installto"));
var avatar = RuntimeUtil.FindAvatarInParents(_installer.transform);
if (avatar != null && GUILayout.Button("Select menu"))
if (avatar != null && GUILayout.Button(G("menuinstall.selectmenu")))
{
AvMenuTreeViewWindow.Show(avatar, menu =>
{
@ -83,32 +80,34 @@ namespace net.fushizen.modular_avatar.core.editor
if (targets.Length == 1)
{
_menuFoldout = EditorGUILayout.Foldout(_menuFoldout, "Show menu contents");
_menuFoldout = EditorGUILayout.Foldout(_menuFoldout, G("menuinstall.showcontents"));
if (_menuFoldout)
{
EditorGUI.indentLevel++;
using (var disabled = new EditorGUI.DisabledScope(true))
{
if (_innerMenuEditor != null) _innerMenuEditor.OnInspectorGUI();
else EditorGUILayout.HelpBox("No menu selected", MessageType.Info);
else EditorGUILayout.HelpBox(S("menuinstall.showcontents.notselected"), MessageType.Info);
}
EditorGUI.indentLevel--;
}
}
_devFoldout = EditorGUILayout.Foldout(_devFoldout, "Developer Options");
_devFoldout = EditorGUILayout.Foldout(_devFoldout, G("menuinstall.devoptions"));
if (_devFoldout)
{
EditorGUI.indentLevel++;
EditorGUILayout.PropertyField(
serializedObject.FindProperty(nameof(ModularAvatarMenuInstaller.menuToAppend)),
new GUIContent("Menu to install")
new GUIContent(G("menuinstall.srcmenu"))
);
EditorGUI.indentLevel--;
}
serializedObject.ApplyModifiedProperties();
Localization.ShowLanguageUI();
}
private void FindMenus()

View File

@ -0,0 +1,65 @@
using UnityEditor;
using UnityEngine;
using static net.fushizen.modular_avatar.core.editor.Localization;
namespace net.fushizen.modular_avatar.core.editor
{
[CustomPropertyDrawer(typeof(MergeAnimatorPathMode))]
class PathModeDrawer : PropertyDrawer
{
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
EditorGUI.BeginProperty(position, label, property);
EditorGUI.BeginChangeCheck();
var value = EditorGUI.Popup(position, label, property.enumValueIndex, new GUIContent[]
{
G("path_mode.Relative"), G("path_mode.Absolute")
});
if (EditorGUI.EndChangeCheck())
{
property.enumValueIndex = value;
}
EditorGUI.EndProperty();
}
}
[CustomEditor(typeof(ModularAvatarMergeAnimator))]
class MergeAnimationEditor : Editor
{
private SerializedProperty prop_animator,
prop_layerType,
prop_deleteAttachedAnimator,
prop_pathMode,
prop_matchAvatarWriteDefaults;
private void OnEnable()
{
prop_animator = serializedObject.FindProperty(nameof(ModularAvatarMergeAnimator.animator));
prop_layerType = serializedObject.FindProperty(nameof(ModularAvatarMergeAnimator.layerType));
prop_deleteAttachedAnimator =
serializedObject.FindProperty(nameof(ModularAvatarMergeAnimator.deleteAttachedAnimator));
prop_pathMode = serializedObject.FindProperty(nameof(ModularAvatarMergeAnimator.pathMode));
prop_matchAvatarWriteDefaults =
serializedObject.FindProperty(nameof(ModularAvatarMergeAnimator.matchAvatarWriteDefaults));
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(prop_animator, G("merge_animator.animator"));
EditorGUILayout.PropertyField(prop_layerType, G("merge_animator.layer_type"));
EditorGUILayout.PropertyField(prop_deleteAttachedAnimator, G("merge_animator.delete_attached_animator"));
EditorGUILayout.PropertyField(prop_pathMode, G("merge_animator.path_mode"));
EditorGUILayout.PropertyField(prop_matchAvatarWriteDefaults,
G("merge_animator.match_avatar_write_defaults"));
serializedObject.ApplyModifiedProperties();
ShowLanguageUI();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e58a4e8774aa4875be67af7940fac8d4
timeCreated: 1665953475

View File

@ -1,19 +1,42 @@
using System;
using UnityEditor;
using UnityEngine;
using static net.fushizen.modular_avatar.core.editor.Localization;
namespace net.fushizen.modular_avatar.core.editor
{
[CustomEditor(typeof(ModularAvatarMergeArmature))]
public class MergeArmatureEditor : Editor
{
private SerializedProperty prop_mergeTarget, prop_prefix, prop_suffix, prop_locked;
private void OnEnable()
{
prop_mergeTarget = serializedObject.FindProperty(nameof(ModularAvatarMergeArmature.mergeTarget));
prop_prefix = serializedObject.FindProperty(nameof(ModularAvatarMergeArmature.prefix));
prop_suffix = serializedObject.FindProperty(nameof(ModularAvatarMergeArmature.suffix));
prop_locked = serializedObject.FindProperty(nameof(ModularAvatarMergeArmature.locked));
}
private void ShowParametersUI()
{
serializedObject.Update();
EditorGUILayout.PropertyField(prop_mergeTarget, G("merge_armature.merge_target"));
EditorGUILayout.PropertyField(prop_prefix, G("merge_armature.prefix"));
EditorGUILayout.PropertyField(prop_suffix, G("merge_armature.suffix"));
EditorGUILayout.PropertyField(prop_locked, G("merge_armature.locked"));
serializedObject.ApplyModifiedProperties();
}
public override void OnInspectorGUI()
{
var target = (ModularAvatarMergeArmature) this.target;
var priorMergeTarget = target.mergeTargetObject;
EditorGUI.BeginChangeCheck();
base.OnInspectorGUI();
ShowParametersUI();
if (EditorGUI.EndChangeCheck())
{
serializedObject.ApplyModifiedProperties();
@ -25,6 +48,8 @@ namespace net.fushizen.modular_avatar.core.editor
target.InferPrefixSuffix();
}
}
Localization.ShowLanguageUI();
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 488c994003974b3ab2796371cf627bca
timeCreated: 1665951353

View File

@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using UnityEditor;
using UnityEngine;
namespace net.fushizen.modular_avatar.core.editor
{
public static class Localization
{
private const string localizationPathGuid = "488c994003974b3ab2796371cf627bca";
private static string localizationPathRoot = AssetDatabase.GUIDToAssetPath(localizationPathGuid);
private static ImmutableDictionary<string, string> SupportedLanguageDisplayNames
= ImmutableDictionary<string, string>.Empty
.Add("en", "English")
.Add("ja", "日本語");
private static ImmutableList<string> SupportedLanguages = new string[] {"en", "ja"}.ToImmutableList();
private static string[] DisplayNames = SupportedLanguages.Select(l =>
{
return SupportedLanguageDisplayNames.TryGetValue(l, out var displayName) ? displayName : l;
}).ToArray();
private static Dictionary<string, ImmutableDictionary<string, string>> Cache
= new Dictionary<string, ImmutableDictionary<string, string>>();
[MenuItem("Tools/Modular Avatar/Reload localizations")]
public static void Reload()
{
Cache.Clear();
}
private static ImmutableDictionary<string, string> GetLocalization(string lang)
{
if (Cache.TryGetValue(lang, out var info))
{
return info;
}
var filename = localizationPathRoot + "/" + lang + ".json";
try
{
var langData = File.ReadAllText(filename);
info = JsonConvert.DeserializeObject<Dictionary<string, string>>(langData).ToImmutableDictionary();
Cache[lang] = info;
return info;
}
catch (Exception e)
{
Debug.LogError("Failed to load language file " + filename);
Debug.LogException(e);
return ImmutableDictionary<string, string>.Empty;
}
}
public static GUIContent G(string key)
{
var tooltip = S(key + ".tooltip", null);
return tooltip != null ? new GUIContent(S(key), tooltip) : new GUIContent(S(key));
}
public static string S(string key)
{
return S(key, key);
}
public static string S(string key, string defValue)
{
var info = GetLocalization(GetSelectedLocalization());
if (info.TryGetValue(key, out var value))
{
return value;
}
else
{
return defValue;
}
}
private static string GetSelectedLocalization()
{
return EditorPrefs.GetString("net.fushizen.modularavatar.lang", "en");
}
public static void ShowLanguageUI()
{
EditorGUILayout.Separator();
var curLang = GetSelectedLocalization();
var curIndex = SupportedLanguages.IndexOf(curLang);
var newIndex = EditorGUILayout.Popup("Editor Language", curIndex, DisplayNames);
if (newIndex != curIndex)
{
EditorPrefs.SetString("net.fushizen.modularavatar.lang", SupportedLanguages[newIndex]);
Reload();
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cf4f643495cb4b20ad3453de4ff8db2f
timeCreated: 1665951380

View File

@ -0,0 +1,41 @@
{
"boneproxy.foldout.advanced": "Advanced",
"boneproxy.target": "Target",
"menuinstall.help.hint_set_menu": "Select one of your avatar's menus to automatically install controls for this prefab.",
"menuinstall.help.hint_bad_menu": "Selected menu asset is not part of your avatar.",
"menuinstall.installto": "Install To",
"menuinstall.installto.tooltip": "The controls for this prefab will be appended to this menu",
"menuinstall.selectmenu": "Select Menu",
"menuinstall.showcontents": "Show menu contents",
"menuinstall.showcontents.notselected": "No menu selected",
"menuinstall.devoptions": "Prefab Developer Options",
"menuinstall.srcmenu": "Menu to Install",
"params.autodetect_header": " Autodetected Parameters ",
"params.internal": "Internal",
"params.pb_prefix": "PhysBones Prefix",
"params.syncmode": "Sync Mode",
"params.saved": "Saved",
"params.default": "Default value",
"params.fieldname": "Field name",
"params.remapto": "Remap to",
"params.remapto.tooltip": "Enter a new name for this parameter here to resolve name conflicts",
"params.devmode": "Show Prefab Developer Options",
"merge_armature.merge_target": "Merge Target",
"merge_armature.merge_target.tooltip": "The armature (or subtree) to merge this object into",
"merge_armature.prefix": "Prefix",
"merge_armature.prefix.tooltip": "Prefix expected on bones in this merged armature",
"merge_armature.suffix": "Suffix",
"merge_armature.suffix.tooltip": "Suffix expected on bones in this merged armature",
"merge_armature.locked": "Lock position",
"merge_armature.locked.tooltip": "Lock the position of this armature's bones to the target armature (and vice versa). Useful for creating animations.",
"path_mode.Relative": "Relative to this object",
"path_mode.Absolute": "Absolute (based on avatar root)",
"merge_animator.animator": "Animator to merge",
"merge_animator.layer_type": "Layer Type",
"merge_animator.delete_attached_animator": "Delete attached animator",
"merge_animator.delete_attached_animator.tooltip": "Delete the animator attached to this object after merging",
"merge_animator.path_mode": "Path Mode",
"merge_animator.path_mode.tooltip": "How to interpret paths in animations. Using relative mode lets you record animations from an animator on this object.",
"merge_animator.match_avatar_write_defaults": "Match Avatar Write Defaults",
"merge_animator.match_avatar_write_defaults.tooltip": "Match the write defaults setting used on the avatar's animator. If the avatar's write defaults settings are inconsistent, the settings on the animator will be left alone."
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8d6cca1a40dd4418b49ef80bf35cdc42
timeCreated: 1665951664

View File

@ -0,0 +1,41 @@
{
"boneproxy.foldout.advanced": "詳細設定",
"boneproxy.target": "ターゲット",
"menuinstall.help.hint_set_menu": "アバターのメニューを指定すれば、このプレハブの設定を自動的に該当メニューに導入します。",
"menuinstall.help.hint_bad_menu": "選択されたメニューがアバターに紐づけされていません。",
"menuinstall.installto": "インストール先",
"menuinstall.installto.tooltip": "このプレハブのメニュー項目がこのメニューに追加されます。",
"menuinstall.selectmenu": "メニューを選択",
"menuinstall.showcontents": "メニュー内容を表示",
"menuinstall.showcontents.notselected": "メニューが選択されていません",
"menuinstall.devoptions": "プレハブ開発者向け設定",
"menuinstall.srcmenu": "インストールされるメニュー",
"params.autodetect_header": " 自動検出されたパラメーター ",
"params.internal": "内部値",
"params.pb_prefix": "PhysBones前置詞",
"params.syncmode": "同期モード",
"params.saved": "保存する",
"params.default": "初期値",
"params.fieldname": "パラメーター名",
"params.remapto": "変更後の名前",
"params.remapto.tooltip": "ここに新しい名前を入れることで、名前かぶりを解消できます",
"params.devmode": "プレハブ開発者向け設定を表示",
"merge_armature.merge_target": "統合先",
"merge_armature.merge_target.tooltip": "このオブジェクトを統合先のアーマチュアに統合します",
"merge_armature.prefix": "前置詞",
"merge_armature.prefix.tooltip": "このオブジェクトの子に付く前置詞",
"merge_armature.suffix": "後置詞",
"merge_armature.suffix.tooltip": "このオブジェクトの子に付く後置詞",
"merge_armature.locked": "位置を固定",
"merge_armature.locked.tooltip": "このオブジェクトのボーンを統合先のボーンに常に相互的に位置を合わせる。アニメーション制作向け",
"path_mode.Relative": "相対的(このオブジェクトからのパースを使用)",
"path_mode.Absolute": "絶対的(アバタールートからのパースを使用)",
"merge_animator.animator": "統合されるアニメーター",
"merge_animator.layer_type": "レイヤー種別",
"merge_animator.delete_attached_animator": "付属アニメーターを削除",
"merge_animator.delete_attached_animator.tooltip": "統合後、このオブジェクトについているアニメーターを削除します",
"merge_animator.path_mode": "パースモード",
"merge_animator.path_mode.tooltip": "アニメーション内のパースを解釈するモード。相対的にすると、このオブジェクトについているアニメーターでアニメーション編集できます",
"merge_animator.match_avatar_write_defaults": "アバターのWrite Defaults設定に合わせる",
"merge_animator.match_avatar_write_defaults.tooltip": "アバターの該当アニメーターのWrite Defaults設定に合わせます。アバター側の設定が矛盾する場合は、統合されるアニメーターのWD値がそのまま採用されます。"
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8c7b0b0edbd548a6913883653b596614
timeCreated: 1665952121

View File

@ -7,7 +7,7 @@ Material:
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: Material
m_Name: FingerpenMaterial
m_Shader: {fileID: 211, guid: 0000000000000000f000000000000000, type: 0}
m_ShaderKeywords:
m_LightmapFlags: 0