diff --git a/UnityProject/Assets/Editor.meta b/UnityProject/Assets/Editor.meta new file mode 100644 index 00000000..34b9f946 --- /dev/null +++ b/UnityProject/Assets/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1b35096feecdb5a4d894aa81a28f72e5 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Editor/I2Localization.meta b/UnityProject/Assets/Editor/I2Localization.meta new file mode 100644 index 00000000..d63e7076 --- /dev/null +++ b/UnityProject/Assets/Editor/I2Localization.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f183cc1bcf4014f4c80a8580caabcd3f +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/Editor/I2Localization/I2Languages.asset b/UnityProject/Assets/Editor/I2Localization/I2Languages.asset new file mode 100644 index 00000000..16a26b7d --- /dev/null +++ b/UnityProject/Assets/Editor/I2Localization/I2Languages.asset @@ -0,0 +1,40 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2f0b0c553be8edd4682e9180fdd13e37, type: 3} + m_Name: I2Languages + m_EditorClassIdentifier: + mSource: + UserAgreesToHaveItOnTheScene: 0 + UserAgreesToHaveItInsideThePluginsFolder: 0 + GoogleLiveSyncIsUptoDate: 1 + mTerms: [] + CaseInsensitiveTerms: 0 + OnMissingTranslation: 1 + mTerm_AppName: + mLanguages: [] + IgnoreDeviceLanguage: 0 + _AllowUnloadingLanguages: 0 + Google_WebServiceURL: + Google_SpreadsheetKey: + Google_SpreadsheetName: + Google_LastUpdatedVersion: + Google_Password: change_this + GoogleUpdateFrequency: 3 + GoogleInEditorCheckFrequency: 2 + GoogleUpdateSynchronization: 1 + GoogleUpdateDelay: 0 + Assets: [] + Spreadsheet_LocalFileName: + Spreadsheet_LocalCSVSeparator: ',' + Spreadsheet_LocalCSVEncoding: utf-8 + Spreadsheet_SpecializationAsRows: 1 + Spreadsheet_SortRows: 1 diff --git a/UnityProject/Assets/Editor/I2Localization/I2Languages.asset.meta b/UnityProject/Assets/Editor/I2Localization/I2Languages.asset.meta new file mode 100644 index 00000000..57c0c7c2 --- /dev/null +++ b/UnityProject/Assets/Editor/I2Localization/I2Languages.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 50fd67a84c64e184c85bf3e1e343ba87 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/GameScripts/Procedure/ProcedureLaunch.cs b/UnityProject/Assets/GameScripts/Procedure/ProcedureLaunch.cs index 9fc96aeb..5d5ca944 100644 --- a/UnityProject/Assets/GameScripts/Procedure/ProcedureLaunch.cs +++ b/UnityProject/Assets/GameScripts/Procedure/ProcedureLaunch.cs @@ -11,6 +11,14 @@ namespace Procedure public class ProcedureLaunch : ProcedureBase { public override bool UseNativeDialog => true; + + private IAudioModule _audioModule; + + protected override void OnInit(ProcedureOwner procedureOwner) + { + _audioModule = ModuleSystem.GetModule(); + base.OnInit(procedureOwner); + } protected override void OnEnter(ProcedureOwner procedureOwner) { @@ -36,49 +44,49 @@ namespace Procedure private void InitLanguageSettings() { - // if (GameModule.Resource.PlayMode == EPlayMode.EditorSimulateMode && GameModule.Base.EditorLanguage == Language.Unspecified) - // { - // // 编辑器资源模式直接使用 Inspector 上设置的语言 - // return; - // } - // - // Language language = GameModule.Localization.Language; - // if (GameModule.Setting.HasSetting(Constant.Setting.Language)) - // { - // try - // { - // string languageString = GameModule.Setting.GetString(Constant.Setting.Language); - // language = (Language)System.Enum.Parse(typeof(Language), languageString); - // } - // catch(System.Exception exception) - // { - // Log.Error("Init language error, reason {0}",exception.ToString()); - // } - // } - // - // if (language != Language.English - // && language != Language.ChineseSimplified - // && language != Language.ChineseTraditional) - // { - // // 若是暂不支持的语言,则使用英语 - // language = Language.English; - // - // GameModule.Setting.SetString(Constant.Setting.Language, language.ToString()); - // GameModule.Setting.Save(); - // } - // - // GameModule.Localization.Language = language; - // Log.Info("Init language settings complete, current language is '{0}'.", language.ToString()); + if (_resourceModule.PlayMode == EPlayMode.EditorSimulateMode && RootModule.Instance.EditorLanguage == Language.Unspecified) + { + // 编辑器资源模式直接使用 Inspector 上设置的语言 + return; + } + + Language language = LocalizationModule.Instance.Language; + if (Utility.PlayerPrefs.HasSetting(Constant.Setting.Language)) + { + try + { + string languageString = Utility.PlayerPrefs.GetString(Constant.Setting.Language); + language = (Language)System.Enum.Parse(typeof(Language), languageString); + } + catch(System.Exception exception) + { + Log.Error("Init language error, reason {0}",exception.ToString()); + } + } + + if (language != Language.English + && language != Language.ChineseSimplified + && language != Language.ChineseTraditional) + { + // 若是暂不支持的语言,则使用英语 + language = Language.English; + + Utility.PlayerPrefs.SetString(Constant.Setting.Language, language.ToString()); + Utility.PlayerPrefs.Save(); + } + + LocalizationModule.Instance.Language = language; + Log.Info("Init language settings complete, current language is '{0}'.", language.ToString()); } private void InitSoundSettings() { - // GameModule.Audio.MusicEnable = !GameModule.Setting.GetBool(Constant.Setting.MusicMuted, false); - // GameModule.Audio.MusicVolume = GameModule.Setting.GetFloat(Constant.Setting.MusicVolume, 1f); - // GameModule.Audio.SoundEnable = !GameModule.Setting.GetBool(Constant.Setting.SoundMuted, false); - // GameModule.Audio.SoundVolume = GameModule.Setting.GetFloat(Constant.Setting.SoundVolume, 1f); - // GameModule.Audio.UISoundEnable = !GameModule.Setting.GetBool(Constant.Setting.UISoundMuted, false); - // GameModule.Audio.UISoundVolume = GameModule.Setting.GetFloat(Constant.Setting.UISoundVolume, 1f); + _audioModule.MusicEnable = !Utility.PlayerPrefs.GetBool(Constant.Setting.MusicMuted, false); + _audioModule.MusicVolume = Utility.PlayerPrefs.GetFloat(Constant.Setting.MusicVolume, 1f); + _audioModule.SoundEnable = !Utility.PlayerPrefs.GetBool(Constant.Setting.SoundMuted, false); + _audioModule.SoundVolume = Utility.PlayerPrefs.GetFloat(Constant.Setting.SoundVolume, 1f); + _audioModule.UISoundEnable = !Utility.PlayerPrefs.GetBool(Constant.Setting.UISoundMuted, false); + _audioModule.UISoundVolume = Utility.PlayerPrefs.GetFloat(Constant.Setting.UISoundVolume, 1f); Log.Info("Init sound settings complete."); } } diff --git a/UnityProject/Assets/TEngine/Editor/Localization.meta b/UnityProject/Assets/TEngine/Editor/Localization.meta new file mode 100644 index 00000000..b95318b5 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: eee776b99f3e76a4c968d6943979829b +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/EditorTools.cs b/UnityProject/Assets/TEngine/Editor/Localization/EditorTools.cs new file mode 100644 index 00000000..c1838534 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/EditorTools.cs @@ -0,0 +1,763 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.SceneManagement; +using Object = UnityEngine.Object; + +namespace TEngine.Localization +{ + public class GUITools + { + static public Color White = Color.white; + static public Color LightGray = Color.Lerp(Color.gray, Color.white, 0.5f); + static public Color DarkGray = Color.Lerp(Color.gray, Color.white, 0.2f); + static public Color LightYellow = Color.Lerp(Color.yellow, Color.white, 0.5f); + + static public GUILayoutOption DontExpandWidth = GUILayout.ExpandWidth(false); + static public GUIContent EmptyContent = new GUIContent (); + + static List mDelayedEditorCallbacks = new List(); + + #region Delayed Editor Callback + + public static void DelayedCall( System.Action action ) + { + if (mDelayedEditorCallbacks.Count == 0) + EditorApplication.update += OnDelayedCall; + + mDelayedEditorCallbacks.Add(action); + } + + static void OnDelayedCall() + { + EditorApplication.update -= OnDelayedCall; + var calls = mDelayedEditorCallbacks.ToArray(); + mDelayedEditorCallbacks.Clear(); + + foreach (var call in calls) + call(); + } + + + #endregion + + #region Header + public delegate void fnOnToggled(bool enabled); + static public bool DrawHeader (string text, string key, bool ShowToggle=false, bool ToggleState=false, fnOnToggled OnToggle = null, string HelpURL=default(string), Color disabledColor = default(Color)) + { + bool state = EditorPrefs.GetBool(key, false); + + bool newState = DrawHeader (text, state, ShowToggle, ToggleState, OnToggle, HelpURL, disabledColor); + + if (state!=newState) EditorPrefs.SetBool(key, newState); + return newState; + } + + static public bool DrawHeader (string text, bool state, bool ShowToggle=false, bool ToggleState=false, fnOnToggled OnToggle = null, string HelpURL=default(string), Color disabledColor = default(Color), bool allowCollapsing = true) + { + GUIStyle Style = new GUIStyle(EditorStyles.foldout); + Style.richText = true; + EditorStyles.foldout.richText = true; + if (state) + { + //GUI.backgroundColor=DarkGray; + GUILayout.BeginVertical(LocalizeInspector.GUIStyle_OldTextArea/*, GUILayout.Height(1)*/); + GUILayout.BeginHorizontal(); + if (!string.IsNullOrEmpty(text)) + { + if (allowCollapsing) + state = GUILayout.Toggle(state, text, Style, GUILayout.ExpandWidth(true)); + else + GUILayout.Label(text, GUILayout.ExpandWidth(true)); + } + + if (!string.IsNullOrEmpty(HelpURL)) + { + if (GUILayout.Button (Icon_Help, EditorStyles.label, GUILayout.ExpandWidth(false))) + Application.OpenURL(HelpURL); + } + if (ShowToggle) + { + GUI.changed = false; + bool newBool = GUILayout.Toggle(ToggleState, "", "OL Toggle", GUILayout.ExpandWidth(false)); + if (GUI.changed && OnToggle!=null) + OnToggle(newBool); + } + GUILayout.EndHorizontal(); + GUILayout.Space(2); + + //GUI.backgroundColor = Color.white; + } + else + { + if (ShowToggle && !ToggleState) + GUI.color = disabledColor; + + GUILayout.BeginHorizontal("Box"); + //GUILayout.BeginHorizontal(EditorStyles.toolbar); + state = GUILayout.Toggle(state, text, Style, GUILayout.ExpandWidth(true)); + if (ShowToggle) + { + GUI.changed = false; + bool newBool = GUILayout.Toggle(ToggleState, "", "OL Toggle", GUILayout.ExpandWidth(false)); + if (GUI.changed && OnToggle!=null) + OnToggle(newBool); + } + GUILayout.EndHorizontal(); + GUI.color = White; + } + return state; + } + + static public void CloseHeader() + { + GUILayout.EndHorizontal(); + } + + public static void OnGUI_Footer(string pluginName, string pluginVersion, string helpURL, string documentationURL, string assetStoreURL) + { + GUILayout.BeginHorizontal(); + string versionTip = ""; + /*if (I2Analytics.HasNewVersion(pluginName)) + { + versionTip = "There is a new version of " + pluginName + ".\nClick here for more details"; + if (GUILayout.Button(new GUIContent("", versionTip), EditorStyles.label, GUILayout.Width(25))) + I2AboutWindow.DoShowScreen(); + + var rect = GUILayoutUtility.GetLastRect(); + rect.yMin = rect.yMax - 25; + rect.xMax = rect.xMin + 25; + rect.y += 3; + GUITools.DrawSkinIcon(rect, "CN EntryWarnIcon", "CN EntryWarn"); + }*/ + + if (GUILayout.Button(new GUIContent("v" + pluginVersion, versionTip), EditorStyles.miniLabel)) + { + Application.OpenURL(assetStoreURL); + //I2AboutWindow.DoShowScreen(); + } + + GUILayout.FlexibleSpace(); + + if (GUILayout.Button("Ask a Question", EditorStyles.miniLabel)) + Application.OpenURL(helpURL); + + GUILayout.Space(10); + + if (GUILayout.Button("Documentation", EditorStyles.miniLabel)) + Application.OpenURL(documentationURL); + GUILayout.EndHorizontal(); + } + + + #endregion + + #region Content + + static public void BeginContents () + { + EditorGUILayout.BeginHorizontal(LocalizeInspector.GUIStyle_OldTextArea, GUILayout.MinHeight(10f)); + GUILayout.Space(2f); + EditorGUILayout.BeginVertical(); + GUILayout.Space(2f); + } + + static public void EndContents () { EndContents(true); } + static public void EndContents ( bool closeHeader ) + { + GUILayout.Space(2f); + EditorGUILayout.EndVertical(); + GUILayout.Space(3f); + GUILayout.EndHorizontal(); + + if (closeHeader) CloseHeader(); + } + + #endregion + + #region Tabs + + static public void DrawTabs( SerializedProperty mProperty, GUIStyle Style=null, int height=25 ) + { + int curIndex = mProperty.enumValueIndex; + int newIndex = DrawTabs( curIndex, mProperty.enumNames, Style, height); + + if (curIndex!=newIndex) + mProperty.enumValueIndex = newIndex; + } + + static public int DrawTabs( int Index, string[] Tabs, GUIStyle Style=null, int height=25, bool expand = true) + { + GUIStyle MyStyle = new GUIStyle(Style!=null?Style:GUI.skin.FindStyle("dragtab")); + MyStyle.fixedHeight=0; + + GUILayout.BeginHorizontal(); + for (int i=0; i0) + { + string text = Tabs[i].Substring(0, idx); + string tooltip = Tabs[i].Substring(idx+1); + if ( GUILayout.Toggle(Index==i, new GUIContent(text, tooltip), MyStyle, GUILayout.Height(height), GUILayout.ExpandWidth(expand)) && Index!=i) + { + Index=i; + GUI.FocusControl(string.Empty); + } + } + else + { + if ( GUILayout.Toggle(Index==i, Tabs[i], MyStyle, GUILayout.Height(height), GUILayout.ExpandWidth(expand)) && Index!=i) + { + Index=i; + GUI.FocusControl(string.Empty); + } + } + } + GUILayout.EndHorizontal(); + return Index; + } + + static public void DrawShadowedTabs( SerializedProperty mProperty, GUIStyle Style=null, int height=25, bool expand=true ) + { + int curIndex = mProperty.enumValueIndex; + int newIndex = DrawShadowedTabs( curIndex, mProperty.enumNames, height, expand); + + if (curIndex!=newIndex) + mProperty.enumValueIndex = newIndex; + } + + static public int DrawShadowedTabs( int Index, string[] Tabs, int height = 25, bool expand=true ) + { + GUI.backgroundColor=Color.Lerp (Color.gray, Color.white, 0.2f); + GUILayout.BeginVertical(LocalizeInspector.GUIStyle_OldTextArea, GUILayout.Height(1)); + GUI.backgroundColor=Color.white; + GUILayout.Space(2); + Index = DrawTabs( Index, Tabs, height: height, expand:expand ); + GUILayout.EndVertical(); + return Index; + } + + static public int DrawTabs( int Index, Texture2D[] Tabs, GUIStyle Style, int height ) + { + GUIStyle MyStyle = new GUIStyle(Style!=null?Style:GUI.skin.FindStyle("dragtab")); + MyStyle.fixedHeight=0; + + //width = Mathf.Max (width, height * Tabs[0].width/(float)Tabs[0].height); + + GUILayout.BeginHorizontal(); + float width = (EditorGUIUtility.currentViewWidth-(MyStyle.border.left+MyStyle.border.right)*(Tabs.Length-1)) / Tabs.Length; + for (int i=0; i1) + GUILayout.Space (18); + } + else + { + if (GUILayout.Button( "\u25B2", EditorStyles.toolbarButton, GUILayout.Width(18))) + MoveUpElement = i; + } + + GUILayout.EndHorizontal(); + } + + GUILayout.BeginHorizontal(EditorStyles.toolbar); + Object NewObj = EditorGUILayout.ObjectField( null, typeof(Object), allowSceneObj, GUILayout.ExpandWidth(true)); + if (testAdd != null) + { + NewObj = testAdd; + } + + if (!allowResources && NewObj != null) + { + var path = AssetDatabase.GetAssetPath(NewObj); + if (path != null && path.Contains("/Resources/")) + NewObj = null; + } + if (NewObj && (allowDuplicates || !ObjectsArrayHasReference(PropArray, NewObj))) + { + int Index = PropArray.arraySize; + PropArray.InsertArrayElementAtIndex( Index ); + PropArray.GetArrayElementAtIndex(Index).objectReferenceValue = NewObj; + hasChanged = true; + } + GUILayout.EndHorizontal(); + + if (DeleteElement>=0) + { + PropArray.DeleteArrayElementAtIndex( DeleteElement ); + //PropArray.DeleteArrayElementAtIndex( DeleteElement ); + hasChanged = true; + } + + if (MoveUpElement>=0) + { + PropArray.MoveArrayElement(MoveUpElement, MoveUpElement-1); + hasChanged = true; + } + + GUILayout.EndVertical(); + return hasChanged; + } + + static public bool ObjectsArrayHasReference(SerializedProperty PropArray, Object obj) + { + for (int i = 0, imax = PropArray.arraySize; i < imax; ++i) + { + SerializedProperty Prop = PropArray.GetArrayElementAtIndex(i); + if (Prop.objectReferenceValue == obj) + return true; + } + return false; + } + + + #endregion + + #region Toggle + + static public int ToggleToolbar( int Index, string[] Options ) + { + GUILayout.BeginHorizontal(); + for (int i=0; i 0) + return false; + + // We are not interested in Prefab, unless they are Prefab Instances + PrefabType pfType = PrefabUtility.GetPrefabType(Obj); + if(pfType == PrefabType.Prefab || pfType == PrefabType.ModelPrefab) + return false; + + // If the database contains the object then its not an scene object, + // but the previous test should get rid of them, so I will just comment this + // unless an false positive object is found in the future + //if (AssetDatabase.Contains(Obj)) + // return false; + + return true;*/ + } + + public static IEnumerable SceneRoots() + { + var prop = new HierarchyProperty(HierarchyType.GameObjects); + var expanded = new int[0]; + while (prop.Next(expanded)) { + yield return prop.pptrValue as GameObject; + } + } + + public static List SceneRootsList() + { + return new List(SceneRoots()); + } + + public static IEnumerable AllSceneObjects() + { + var queue = new Queue(); + + foreach (var root in SceneRoots()) { + var tf = root.transform; + yield return tf; + queue.Enqueue(tf); + } + + while (queue.Count > 0) { + foreach (Transform child in queue.Dequeue()) { + yield return child; + queue.Enqueue(child); + } + } + } + + public static string GetScenePath(Transform tr) + { + if (tr==null) + return string.Empty; + + string path = tr.name; + while (tr.parent != null) + { + tr = tr.parent; + path = tr.name + "/" + path; + } + return path; + } + + public static Transform FindObjectInEditor( string scenePath ) + { + if (string.IsNullOrEmpty(scenePath)) + return null; + + int index = scenePath.IndexOfAny("/\\".ToCharArray()); + string first = index<0 ? scenePath : scenePath.Substring(0, index); + + foreach (var root in AllSceneObjects()) + if (root.name==first) + { + if (index<0) + return root; + + return root.Find(scenePath.Substring(index+1)); + } + return null; + } + + + public static GUIContent Icon_Help { + get{ + if (mIconHelp == null) + mIconHelp = EditorGUIUtility.IconContent("_Help"); + return mIconHelp; + } + } + static GUIContent mIconHelp; + + public static GUIStyle FindSkinStyle(string name) + { + var allStyles = GUI.skin.customStyles; + for (int i = 0, imax = allStyles.Length; i < imax; ++i) + { + if (allStyles[i].name == name) + return allStyles[i]; + } + return null; + } + public static void DrawSkinIcon(Rect rect, params string[] iconNames) + { + foreach (var icon in iconNames) + { + var style = FindSkinStyle(icon); + if (style == null || style.normal == null || style.normal.background == null) + continue; + + GUI.DrawTexture(rect, style.normal.background); + return; + } + //Debug.Log("unable to find icon"); + } + + #endregion + + #region Angle Drawer + private static Vector2 mAngle_lastMousePosition; + static Texture mAngle_TextureCircle; + static Texture pAngle_TextureCircle { + get{ + if (mAngle_TextureCircle) return mAngle_TextureCircle; + mAngle_TextureCircle = GUI.skin.GetStyle("CN CountBadge").normal.background; + return mAngle_TextureCircle; + } + } + + public static float FloatAngle(Rect rect, float value) + { + return FloatAngle(rect, value, -1, -1, -1); + } + + public static float FloatAngle(Rect rect, float value, float snap) + { + return FloatAngle(rect, value, snap, -1, -1); + } + + public static float FloatAngle(Rect rect, float value, float snap, float min, float max) + { + int id = GUIUtility.GetControlID(FocusType.Passive, rect); + + Rect knobRect = new Rect(rect.x, rect.y, rect.height, rect.height); + + float delta; + if (min != max) + delta = (max - min) / 360; + else + delta = 1; + + if (UnityEngine.Event.current != null) + { + if (UnityEngine.Event.current.type == EventType.MouseDown && knobRect.Contains(UnityEngine.Event.current.mousePosition)) + { + GUIUtility.hotControl = id; + mAngle_lastMousePosition = UnityEngine.Event.current.mousePosition; + } + else if (UnityEngine.Event.current.type == EventType.MouseUp && GUIUtility.hotControl == id) + GUIUtility.hotControl = -1; + else if (UnityEngine.Event.current.type == EventType.MouseDrag && GUIUtility.hotControl == id) + { + Vector2 move = mAngle_lastMousePosition - UnityEngine.Event.current.mousePosition; + value += delta * (-move.x - move.y); + + if (snap > 0) + { + float mod = value % snap; + + if (mod < delta * 3 || Mathf.Abs(mod - snap) < delta * 3) + value = Mathf.Round(value / snap) * snap; + } + + mAngle_lastMousePosition = UnityEngine.Event.current.mousePosition; + GUI.changed = true; + } + } + + if (pAngle_TextureCircle) GUI.DrawTexture(knobRect, pAngle_TextureCircle); + Matrix4x4 matrix = GUI.matrix; + + if (min != max) + GUIUtility.RotateAroundPivot(value * (360 / (max - min)), knobRect.center); + else + GUIUtility.RotateAroundPivot(value, knobRect.center); + + knobRect.height = 5; + knobRect.width = 5; + if (pAngle_TextureCircle) GUI.DrawTexture(knobRect, pAngle_TextureCircle); + GUI.matrix = matrix; + + Rect label = new Rect(rect.x + rect.height, rect.y + rect.height / 2 - 9, rect.height, 18); + value = EditorGUI.FloatField(label, value); + + if (min != max) + value = Mathf.Clamp(value, min, max); + + return value; + } + + public static float AngleCircle(Rect rect, float angle, float snap, float min, float max, Texture background=null, Texture knobLine=null) + { + Rect knobRect = new Rect(rect.x, rect.y, rect.height, rect.height); + + float delta; + if (min != max) + delta = (max - min) / 360; + else + delta = 1; + + if (UnityEngine.Event.current != null && GUIUtility.hotControl<=0 && (UnityEngine.Event.current.type==EventType.MouseDown || UnityEngine.Event.current.type==EventType.MouseDrag) && knobRect.Contains(UnityEngine.Event.current.mousePosition)) + { + angle = Vector2.Angle( Vector2.right, UnityEngine.Event.current.mousePosition-knobRect.center); + if (UnityEngine.Event.current.mousePosition.y 0) + { + float mod = Mathf.Repeat(angle, snap); + + if (mod < delta * 3 || Mathf.Abs(mod - snap) < delta * 3) + angle = Mathf.Round(angle / snap) * snap; + } + + GUI.changed = true; + } + + if (background==null) background = pAngle_TextureCircle; + if (background) GUI.DrawTexture (knobRect, background); + + Matrix4x4 matrix = GUI.matrix; + + if (min != max) + GUIUtility.RotateAroundPivot(angle * (360 / (max - min))+90, knobRect.center); + else + GUIUtility.RotateAroundPivot(angle+90, knobRect.center); + + float Radius = Mathf.Min (knobRect.width, knobRect.height) * 0.5f; + knobRect.x = knobRect.x + 0.5f * knobRect.width - 4; + knobRect.y += 2; + knobRect.width = 8; + knobRect.height = Radius+2; + if (knobLine == null) + knobLine = GUI.skin.FindStyle ("MeBlendPosition").normal.background; + if (knobLine) GUI.DrawTexture(knobRect, knobLine); + GUI.matrix = matrix; + + return Mathf.Repeat(angle, 360); + } + #endregion + + #region Unity Version branching + + public static string Editor_GetCurrentScene() + { + #if UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 + return EditorApplication.currentScene; + #else + return SceneManager.GetActiveScene().path; + #endif + } + + public static void Editor_MarkSceneDirty() + { + #if UNITY_5_3 || UNITY_5_3_OR_NEWER + EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene()); + #else + EditorApplication.MarkSceneDirty(); + #endif + } + + public static void Editor_SaveScene() + { + #if UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 + EditorApplication.SaveScene (); + #else + EditorSceneManager.SaveOpenScenes(); + #endif + } + + public static void Editor_OpenScene( string sceneName ) + { + #if UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 + EditorApplication.OpenScene( sceneName ); + #else + EditorSceneManager.OpenScene(sceneName); + #endif + } + + #endregion + + #region Reflection + static public object Reflection_InvokeMethod ( object instanceObject, string methodName, params object[] p_args ) + { + BindingFlags _flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static; + MethodInfo mi = instanceObject.GetType().GetMethods( _flags ).Where( x => x.Name==methodName ).FirstOrDefault(); + if (mi == null) return null; + return mi.Invoke( instanceObject, p_args ); + } + static public object Reflection_InvokeMethod ( Type targetType, string methodName, params object[] p_args ) + { + BindingFlags _flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static; + MethodInfo mi = targetType.GetMethods( _flags ).Where( x => x.Name==methodName ).FirstOrDefault(); + if (mi == null) return null; + return mi.Invoke( null, p_args ); + } + + + public static object s_RecycledEditor; + public static string TextField ( Rect position, string text, int maxLength, GUIStyle style, int controlID ) + { + if (s_RecycledEditor==null) + { + FieldInfo info = typeof(EditorGUI).GetField("s_RecycledEditor", BindingFlags.NonPublic | BindingFlags.Static); + s_RecycledEditor = info.GetValue(null); + } + + if (s_RecycledEditor == null) + return ""; + + return Reflection_InvokeMethod( typeof( EditorGUI ), "DoTextField", s_RecycledEditor, controlID, position, text, style, null, false, false, false, false ) as string; + } + + static public void RepaintInspectors() + { + EditorApplication.update -= RepaintInspectors; + var assemblyEditor = Assembly.GetAssembly(typeof(UnityEditor.Editor)); + var typeInspectorWindow = assemblyEditor.GetType("UnityEditor.InspectorWindow"); + typeInspectorWindow.GetMethod("RepaintAllInspectors", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, null); + } + + public static void ScheduleRepaintInspectors() + { + EditorApplication.update += RepaintInspectors; + } + + + #endregion + + #if UNITY_2022_3_OR_NEWER + public const string Style_ToolbarSearchTextField = "ToolbarSearchTextField"; + public const string Style_ToolbarSearchCancelButtonEmpty = "ToolbarSearchCancelButtonEmpty"; + public const string Style_ToolbarSearchCancelButton = "ToolbarSearchCancelButton"; + #else + public const string Style_ToolbarSearchTextField = "ToolbarSeachTextField"; + public const string Style_ToolbarSearchCancelButtonEmpty = "ToolbarSeachCancelButtonEmpty"; + public const string Style_ToolbarSearchCancelButton = "ToolbarSeachCancelButton"; + #endif + } +} diff --git a/UnityProject/Assets/TEngine/Editor/Localization/EditorTools.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/EditorTools.cs.meta new file mode 100644 index 00000000..de59c6b6 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/EditorTools.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: df33c1000ac895241a433812e40a2096 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors.meta b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors.meta new file mode 100644 index 00000000..8a09e58e --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: a500716e59f61824ba1fa6b418ce31a7 +folderAsset: yes +timeCreated: 1461137613 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LanguageSourceAssetInspector.cs b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LanguageSourceAssetInspector.cs new file mode 100644 index 00000000..f6187b4c --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LanguageSourceAssetInspector.cs @@ -0,0 +1,20 @@ +using UnityEditor; + +namespace TEngine.Localization +{ + [CustomEditor(typeof(LanguageSourceAsset))] + public class LanguageSourceAssetInspector : LocalizationEditor + { + void OnEnable() + { + var newSource = target as LanguageSourceAsset; + SerializedProperty propSource = serializedObject.FindProperty("mSource"); + + Custom_OnEnable(newSource.mSource, propSource); + } + public override LanguageSourceData GetSourceData() + { + return (target as LanguageSourceAsset).mSource; + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LanguageSourceAssetInspector.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LanguageSourceAssetInspector.cs.meta new file mode 100644 index 00000000..ef8182c4 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LanguageSourceAssetInspector.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: f03a75bf70a306a4fb36646f24c1c1f1 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LanguageSourceInspector.cs b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LanguageSourceInspector.cs new file mode 100644 index 00000000..b11b01ae --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LanguageSourceInspector.cs @@ -0,0 +1,22 @@ +using UnityEditor; + +namespace TEngine.Localization +{ + [CustomEditor(typeof(LanguageSource))] + public class LanguageSourceInspector : LocalizationEditor + { + void OnEnable() + { + var newSource = target as LanguageSource; + SerializedProperty propSource = serializedObject.FindProperty("mSource"); + + Custom_OnEnable(newSource.mSource, propSource); + } + + public override LanguageSourceData GetSourceData() + { + return (target as LanguageSource).mSource; + } + + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LanguageSourceInspector.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LanguageSourceInspector.cs.meta new file mode 100644 index 00000000..ce993bdf --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LanguageSourceInspector.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: a441ed994a43a0a4a9d33be67a8d3f15 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizationEditor.cs b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizationEditor.cs new file mode 100644 index 00000000..a9c4cfcc --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizationEditor.cs @@ -0,0 +1,200 @@ +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + public abstract partial class LocalizationEditor : UnityEditor.Editor + { + #region Variables + + SerializedProperty mProp_Assets, mProp_Languages, + mProp_Google_WebServiceURL, mProp_GoogleUpdateFrequency, mProp_GoogleUpdateDelay, mProp_Google_SpreadsheetKey, mProp_Google_SpreadsheetName, mProp_Google_Password, + mProp_Spreadsheet_LocalFileName, mProp_Spreadsheet_LocalCSVSeparator, mProp_CaseInsensitiveTerms, mProp_Spreadsheet_LocalCSVEncoding, + mProp_OnMissingTranslation, mProp_AppNameTerm, mProp_IgnoreDeviceLanguage, mProp_Spreadsheet_SpecializationAsRows, mProp_Spreadsheet_SortRows, mProp_GoogleInEditorCheckFrequency, + mProp_HighlightLocalizedTargets, mProp_GoogleLiveSyncIsUptoDate, mProp_AllowUnloadingLanguages, mProp_GoogleUpdateSynchronization; + + public static LanguageSourceData mLanguageSource; + public static Object mLanguageSourceObject; + public static LocalizationEditor mLanguageSourceEditor; + public static UnityEditor.Editor mCurrentInspector; + + static bool mIsParsing; // This is true when the editor is opening several scenes to avoid reparsing objects + + #endregion + + #region Variables GUI + + GUIStyle Style_ToolBar_Big, Style_ToolBarButton_Big; + + + public GUISkin CustomSkin; + + static Vector3 mScrollPos_Languages; + public static string mLanguages_NewLanguage = ""; + + #endregion + + #region Styles + + public static GUIStyle Style_WrapTextField { + get{ + if (mStyle_WrapTextField==null) + { + mStyle_WrapTextField = new GUIStyle(EditorStyles.textArea); + mStyle_WrapTextField.wordWrap = true; + mStyle_WrapTextField.fixedHeight = 0; + } + return mStyle_WrapTextField; + } + } + static GUIStyle mStyle_WrapTextField; + + #endregion + + #region Inspector + + public void Custom_OnEnable( LanguageSourceData sourceData, SerializedProperty propSource) + { + bool ForceParse = mLanguageSource != sourceData; + + mLanguageSource = sourceData; + mLanguageSourceEditor = this; + mCurrentInspector = this; + + if (!LocalizationManager.Sources.Contains(mLanguageSource)) + LocalizationManager.UpdateSources(); + + mProp_Assets = propSource.FindPropertyRelative("Assets"); + mProp_Languages = propSource.FindPropertyRelative("mLanguages"); + mProp_Google_WebServiceURL = propSource.FindPropertyRelative("Google_WebServiceURL"); + mProp_GoogleUpdateFrequency = propSource.FindPropertyRelative("GoogleUpdateFrequency"); + mProp_GoogleUpdateSynchronization = propSource.FindPropertyRelative("GoogleUpdateSynchronization"); + mProp_GoogleInEditorCheckFrequency = propSource.FindPropertyRelative("GoogleInEditorCheckFrequency"); + mProp_GoogleUpdateDelay = propSource.FindPropertyRelative("GoogleUpdateDelay"); + mProp_Google_SpreadsheetKey = propSource.FindPropertyRelative("Google_SpreadsheetKey"); + mProp_Google_SpreadsheetName = propSource.FindPropertyRelative("Google_SpreadsheetName"); + mProp_Google_Password = propSource.FindPropertyRelative("Google_Password"); + mProp_CaseInsensitiveTerms = propSource.FindPropertyRelative("CaseInsensitiveTerms"); + mProp_Spreadsheet_LocalFileName = propSource.FindPropertyRelative("Spreadsheet_LocalFileName"); + mProp_Spreadsheet_LocalCSVSeparator = propSource.FindPropertyRelative("Spreadsheet_LocalCSVSeparator"); + mProp_Spreadsheet_LocalCSVEncoding = propSource.FindPropertyRelative("Spreadsheet_LocalCSVEncoding"); + mProp_Spreadsheet_SpecializationAsRows = propSource.FindPropertyRelative("Spreadsheet_SpecializationAsRows"); + mProp_Spreadsheet_SortRows = propSource.FindPropertyRelative("Spreadsheet_SortRows"); + mProp_OnMissingTranslation = propSource.FindPropertyRelative("OnMissingTranslation"); + mProp_AppNameTerm = propSource.FindPropertyRelative("mTerm_AppName"); + mProp_IgnoreDeviceLanguage = propSource.FindPropertyRelative("IgnoreDeviceLanguage"); + mProp_GoogleLiveSyncIsUptoDate = propSource.FindPropertyRelative("GoogleLiveSyncIsUptoDate"); + mProp_AllowUnloadingLanguages = propSource.FindPropertyRelative("_AllowUnloadingLanguages"); + + if (!mIsParsing) + { + if (string.IsNullOrEmpty(mLanguageSource.Google_SpreadsheetKey)) + mSpreadsheetMode = eSpreadsheetMode.Local; + else + mSpreadsheetMode = eSpreadsheetMode.Google; + + mCurrentViewMode = mLanguageSource.mLanguages.Count>0 ? eViewMode.Keys : eViewMode.Languages; + + UpdateSelectedKeys(); + + if (ForceParse || mParsedTerms.Count < mLanguageSource.mTerms.Count) + { + mSelectedCategories.Clear(); + ParseTerms(true, false, true); + } + } + ScheduleUpdateTermsToShowInList(); + LoadSelectedCategories(); + //UpgradeManager.EnablePlugins(); + } + + void OnDisable() + { + //LocalizationManager.LocalizeAll(); + SaveSelectedCategories(); + mLanguageSourceEditor = null; + if (mCurrentInspector==this) mCurrentInspector = null; + } + + + void UpdateSelectedKeys() + { + // Remove all keys that are not in this source + string trans; + for (int i=mSelectedKeys.Count-1; i>=0; --i) + if (!mLanguageSource.TryGetTranslation(mSelectedKeys[i], out trans)) + mSelectedKeys.RemoveAt(i); + + // Remove all Categories that are not in this source + /*var mCateg = mLanguageSource.GetCategories(); + for (int i=mSelectedCategories.Count-1; i>=0; --i) + if (!mCateg.Contains(mSelectedCategories[i])) + mSelectedCategories.RemoveAt(i); + if (mSelectedCategories.Count==0) + mSelectedCategories = mCateg;*/ + + if (mSelectedScenes.Count==0) + mSelectedScenes.Add (Editor_GetCurrentScene()); + } + + public override void OnInspectorGUI() + { + // Load Test: + /*if (mLanguageSource.mTerms.Count<40000) + { + mLanguageSource.mTerms.Clear(); + for (int i=0; i<40020; ++i) + mLanguageSource.AddTerm("ahh"+i.ToString("00000"), eTermType.Text, false); + mLanguageSource.UpdateDictionary(); + }*/ + //Profiler.maxNumberOfSamplesPerFrame = -1; // REMOVE --------------------------------------------------- + + mIsParsing = false; + + //#if UNITY_5_6_OR_NEWER + // serializedObject.UpdateIfRequiredOrScript(); + //#else + // serializedObject.UpdateIfDirtyOrScript(); + //#endif + + if (mLanguageSource.mTerms.Count<1000) + Undo.RecordObject(target, "LanguageSource"); + + //GUI.backgroundColor = Color.Lerp (Color.black, Color.gray, 1); + //GUILayout.BeginVertical(LocalizeInspector.GUIStyle_Background); + //GUI.backgroundColor = Color.white; + + if (GUILayout.Button("Language Source", LocalizeInspector.GUIStyle_Header)) + { + Application.OpenURL(LocalizeInspector.HelpURL_Documentation); + } + + InitializeStyles(); + + GUILayout.Space(10); + + //GUI.backgroundColor = Color.Lerp(GUITools.LightGray, Color.white, 0.5f); + //GUILayout.BeginVertical(LocalizeInspector.GUIStyle_Background); + //GUI.backgroundColor = Color.white; + OnGUI_Main(); + //GUILayout.EndVertical(); + + GUILayout.Space (10); + GUILayout.FlexibleSpace(); + + GUITools.OnGUI_Footer("I2 Localization", LocalizationManager.GetVersion(), LocalizeInspector.HelpURL_forum, LocalizeInspector.HelpURL_Documentation, LocalizeInspector.HelpURL_AssetStore); + + //GUILayout.EndVertical(); + + serializedObject.ApplyModifiedProperties(); + if (UnityEngine.Event.current.type == EventType.Repaint) + { + mTestAction = eTest_ActionType.None; + mTestActionArg = null; + mTestActionArg2 = null; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizationEditor.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizationEditor.cs.meta new file mode 100644 index 00000000..34e4a0e9 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizationEditor.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 90c932abd0dc445448366dfe101408ba +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizationParamsManagerInspector.cs b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizationParamsManagerInspector.cs new file mode 100644 index 00000000..b8603c12 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizationParamsManagerInspector.cs @@ -0,0 +1,107 @@ +using UnityEditor; +using UnityEditorInternal; +using UnityEngine; + +namespace TEngine.Localization +{ + [CustomEditor(typeof(LocalizationParamsManager))] + public class LocalizationParamsManagerInspector : UnityEditor.Editor + { + private ReorderableList mList; + private SerializedProperty mProp_IsGlobalManager; + + + private ReorderableList getList(SerializedObject serObject) + { + if (mList == null) { + mList = new ReorderableList (serObject, serObject.FindProperty ("_Params"), true, true, true, true); + mList.drawElementCallback = drawElementCallback; + mList.drawHeaderCallback = drawHeaderCallback; + mList.onAddCallback = addElementCallback; + mList.onRemoveCallback = removeElementCallback; + } + else + { + mList.serializedProperty = serObject.FindProperty ("_Params"); + } + return mList; + } + + private void addElementCallback( ReorderableList list ) + { + serializedObject.ApplyModifiedProperties(); + var objParams = target as LocalizationParamsManager; + objParams._Params.Add(new LocalizationParamsManager.ParamValue()); + list.index = objParams._Params.Count - 1; + serializedObject.Update(); + } + + private void removeElementCallback( ReorderableList list ) + { + if (list.index < 0) + return; + serializedObject.ApplyModifiedProperties(); + var objParams = target as LocalizationParamsManager; + objParams._Params.RemoveAt(list.index); + serializedObject.Update(); + } + + private void drawHeaderCallback(Rect rect) + { + GUI.Label(rect, "Parameters:"); + } + + private void drawElementCallback(Rect rect, int index, bool isActive, bool isFocused) + { + var serializedElement = mList.serializedProperty.GetArrayElementAtIndex (index); + var content = new GUIContent (); + + Rect r = rect; r.xMax = r.xMin+40; + GUI.Label(r, "Name"); + + r = rect; r.xMax = (r.xMax + r.xMin)/2 - 2; r.xMin = r.xMin+40; + EditorGUI.PropertyField (r, serializedElement.FindPropertyRelative ("Name"),content); + + r = rect; r.xMin = (r.xMax + r.xMin) / 2 + 2; r.xMax = r.xMin+40; + GUI.Label(r, "Value"); + + r = rect; r.xMin = (r.xMax + r.xMin)/2 + 2 + 40; + EditorGUI.PropertyField (r, serializedElement.FindPropertyRelative ("Value"), content); + } + + void OnEnable() + { + mList = getList(serializedObject); + mProp_IsGlobalManager = serializedObject.FindProperty("_IsGlobalManager"); + } + public override void OnInspectorGUI() + { + #if UNITY_5_6_OR_NEWER + serializedObject.UpdateIfRequiredOrScript(); + #else + serializedObject.UpdateIfDirtyOrScript(); + #endif + + GUI.backgroundColor = Color.Lerp (Color.black, Color.gray, 1); + GUILayout.BeginVertical(LocalizeInspector.GUIStyle_Background); + GUI.backgroundColor = Color.white; + + if (GUILayout.Button("Dynamic Parameters", LocalizeInspector.GUIStyle_Header)) + { + Application.OpenURL(LocalizeInspector.HelpURL_Documentation); + } + + GUILayout.Space(5); + mProp_IsGlobalManager.boolValue = EditorGUILayout.Popup(new GUIContent("Manager Type", "Local Manager only apply parameters to the Localize component in the same GameObject\n\nGlobal Manager apply parameters to all Localize components"), mProp_IsGlobalManager.boolValue ? 1 : 0, new[] { new GUIContent("Local"), new GUIContent("Global") }) == 1; + + + GUILayout.Space(5); + mList.DoLayoutList(); + + //EditorGUILayout.PropertyField(serializedObject.FindProperty("_AutoRegister"), new GUIContent("Auto Register")); + + GUILayout.EndVertical(); + serializedObject.ApplyModifiedProperties(); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizationParamsManagerInspector.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizationParamsManagerInspector.cs.meta new file mode 100644 index 00000000..8c96c158 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizationParamsManagerInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 93f1f9aecf6f7ed40ad1a082c22c47e5 +timeCreated: 1468111539 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizeDropdownInspector.cs b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizeDropdownInspector.cs new file mode 100644 index 00000000..0244d51a --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizeDropdownInspector.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEditorInternal; +using UnityEngine; + +namespace TEngine.Localization +{ + #if !UNITY_5_0 && !UNITY_5_1 + + [CustomEditor(typeof(LocalizeDropdown))] + public class LocalizeDropdownInspector : UnityEditor.Editor + { + private ReorderableList mList; + + private List terms; + + private ReorderableList getList(SerializedObject serObject) + { + if (mList == null) { + mList = new ReorderableList (serObject, serObject.FindProperty ("_Terms"), true, true, true, true); + mList.drawElementCallback = drawElementCallback; + mList.drawHeaderCallback = drawHeaderCallback; + mList.onAddCallback = addElementCallback; + mList.onRemoveCallback = removeElementCallback; + } + else + { + mList.serializedProperty = serObject.FindProperty ("_Terms"); + } + return mList; + } + + private void addElementCallback( ReorderableList list ) + { + serializedObject.ApplyModifiedProperties(); + + var objParams = target as LocalizeDropdown; + objParams._Terms.Add(string.Empty); + + list.index = objParams._Terms.Count - 1; + + serializedObject.Update(); + } + + private void removeElementCallback( ReorderableList list ) + { + if (list.index < 0) + return; + serializedObject.ApplyModifiedProperties(); + + var objParams = target as LocalizeDropdown; + objParams._Terms.RemoveAt(list.index); + + serializedObject.Update(); + } + + private void drawHeaderCallback(Rect rect) + { + GUI.Label(rect, "Terms:"); + } + + private void drawElementCallback(Rect rect, int index, bool isActive, bool isFocused) + { + var serializedElement = mList.serializedProperty.GetArrayElementAtIndex (index); + + EditorGUI.BeginChangeCheck (); + + var prvIndex = serializedElement.stringValue == "-" || serializedElement.stringValue == "" ? terms.Count - 1 : + serializedElement.stringValue == " " ? terms.Count - 2 : + terms.IndexOf(serializedElement.stringValue); + + var newIndex = EditorGUI.Popup(rect, prvIndex, terms.ToArray()); + + if (EditorGUI.EndChangeCheck ()) + { + if (newIndex == terms.Count - 1) + serializedElement.stringValue = "-"; + else + if (newIndex < 0 || newIndex == terms.Count - 2) + serializedElement.stringValue = string.Empty; + else + serializedElement.stringValue = terms[newIndex]; + } + } + + void OnEnable() + { + mList = getList(serializedObject); + } + + public override void OnInspectorGUI() + { + #if UNITY_5_6_OR_NEWER + serializedObject.UpdateIfRequiredOrScript(); + #else + serializedObject.UpdateIfDirtyOrScript(); + #endif + terms = LocalizationManager.GetTermsList (); + terms.Sort(StringComparer.OrdinalIgnoreCase); + terms.Add(""); + terms.Add(""); + terms.Add(""); + + GUI.backgroundColor = Color.Lerp (Color.black, Color.gray, 1); + GUILayout.BeginVertical(LocalizeInspector.GUIStyle_Background); + GUI.backgroundColor = Color.white; + + if (GUILayout.Button("Localize DropDown", LocalizeInspector.GUIStyle_Header)) + { + Application.OpenURL(LocalizeInspector.HelpURL_Documentation); + } + + + GUILayout.Space(5); + mList.DoLayoutList(); + + GUILayout.Space (10); + + GUITools.OnGUI_Footer("I2 Localization", LocalizationManager.GetVersion(), LocalizeInspector.HelpURL_forum, LocalizeInspector.HelpURL_Documentation, LocalizeInspector.HelpURL_AssetStore); + + EditorGUIUtility.labelWidth = 0; + + + GUILayout.EndVertical(); + serializedObject.ApplyModifiedProperties(); + terms = null; + } + } + #endif +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizeDropdownInspector.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizeDropdownInspector.cs.meta new file mode 100644 index 00000000..fb4a5ed1 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizeDropdownInspector.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 65bbef08e6e42d24d9834945c3769202 +timeCreated: 1468111539 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizeInspector.cs b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizeInspector.cs new file mode 100644 index 00000000..c80e822b --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizeInspector.cs @@ -0,0 +1,896 @@ +//#define UGUI +//#define NGUI +//#define DFGUI + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using UnityEditor; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace TEngine.Localization +{ + [CustomEditor(typeof(Localize))] + [CanEditMultipleObjects] + public class LocalizeInspector : UnityEditor.Editor + { + #region Variables + + Localize mLocalize; + SerializedProperty mProp_mTerm, mProp_mTermSecondary, + mProp_TranslatedObjects, mProp_LocalizeOnAwake, mProp_AlwaysForceLocalize, mProp_AllowLocalizedParameters, mProp_AllowParameters, + mProp_IgnoreRTL, mProp_MaxCharactersInRTL, mProp_CorrectAlignmentForRTL, mProp_IgnoreNumbersInRTL, mProp_TermSuffix, mProp_TermPrefix, mProp_SeparateWords, + mProp_CallbackEvent; + + + bool mAllowEditKeyName; + string mNewKeyName = ""; + + string[] mTermsArray; + + + public static string HelpURL_forum = "http://goo.gl/Uiyu8C";//http://www.inter-illusion.com/forum/i2-localization"; + public static string HelpURL_Documentation = "http://www.inter-illusion.com/assets/I2LocalizationManual/I2LocalizationManual.html"; + public static string HelpURL_Tutorials = "http://inter-illusion.com/tools/i2-localization"; + public static string HelpURL_ReleaseNotes = "http://inter-illusion.com/forum/i2-localization/26-release-notes"; + public static string HelpURL_AssetStore = "https://www.assetstore.unity3d.com/#!/content/14884"; + + public static LocalizeInspector mLocalizeInspector; + #endregion + + #region Inspector + + void OnEnable() + { + mLocalize = (Localize)target; + mLocalizeInspector = this; + LocalizationEditor.mCurrentInspector = this; + mProp_mTerm = serializedObject.FindProperty("mTerm"); + mProp_mTermSecondary = serializedObject.FindProperty("mTermSecondary"); + mProp_TranslatedObjects = serializedObject.FindProperty("TranslatedObjects"); + mProp_IgnoreRTL = serializedObject.FindProperty("IgnoreRTL"); + mProp_SeparateWords = serializedObject.FindProperty("AddSpacesToJoinedLanguages"); + mProp_MaxCharactersInRTL = serializedObject.FindProperty ("MaxCharactersInRTL"); + mProp_IgnoreNumbersInRTL = serializedObject.FindProperty("IgnoreNumbersInRTL"); + mProp_CorrectAlignmentForRTL = serializedObject.FindProperty ("CorrectAlignmentForRTL"); + mProp_LocalizeOnAwake = serializedObject.FindProperty("LocalizeOnAwake"); + mProp_AlwaysForceLocalize = serializedObject.FindProperty("AlwaysForceLocalize"); + mProp_TermSuffix = serializedObject.FindProperty("TermSuffix"); + mProp_TermPrefix = serializedObject.FindProperty("TermPrefix"); + mProp_CallbackEvent = serializedObject.FindProperty("LocalizeEvent"); + mProp_AllowLocalizedParameters = serializedObject.FindProperty("AllowLocalizedParameters"); + mProp_AllowParameters = serializedObject.FindProperty("AllowParameters"); + + + if (LocalizationManager.Sources.Count==0) + LocalizationManager.UpdateSources(); + //LocalizationEditor.ParseTerms (true); + + //mGUI_ShowReferences = (mLocalize.TranslatedObjects!=null && mLocalize.TranslatedObjects.Length>0); + //mGUI_ShowCallback = (mLocalize.LocalizeCallBack.Target!=null); + //mGUI_ShowTems = true; + LocalizationEditor.mKeysDesc_AllowEdit = false; + GUI_SelectedTerm = 0; + mNewKeyName = mLocalize.Term; + + if (mLocalize.Source != null) + LocalizationEditor.mLanguageSource = mLocalize.Source.SourceData; + else + { + if (LocalizationManager.Sources.Count==0) + LocalizationManager.UpdateSources(); + LocalizationEditor.mLanguageSource = LocalizationManager.GetSourceContaining( mLocalize.Term ); + } + + //UpgradeManager.EnablePlugins(); + LocalizationEditor.ApplyInferredTerm (mLocalize); + RemoveUnusedReferences(mLocalize); + } + + void OnDisable() + { + mLocalizeInspector = null; + if (LocalizationEditor.mCurrentInspector == this) LocalizationEditor.mCurrentInspector = null; + + + if (mLocalize == null) + return; + + //#if TextMeshPro + //string previous = null; + + //if (!Application.isPlaying && !string.IsNullOrEmpty(mLocalize.TMP_previewLanguage)) + //{ + // previous = LocalizationManager.CurrentLanguage; + // LocalizationManager.PreviewLanguage( mLocalize.TMP_previewLanguage ); + //} + //#endif + + //mLocalize.OnLocalize(); + + // Revert the preview language + // except when in TMPro and not changing to another GameObject (TMPro has a bug where any change causes the inspector to Disable and Enable) + if (!mLocalize.mLocalizeTargetName.Contains("LocalizeTarget_TextMeshPro") || Selection.activeGameObject==null || !Selection.gameObjects.Contains(mLocalize.gameObject)) + { + LocalizationManager.LocalizeAll(); + } + + //#if TextMeshPro + //if (!string.IsNullOrEmpty(previous)) + //{ + // LocalizationManager.PreviewLanguage(previous); + // mLocalize.TMP_previewLanguage = null; + //} + //#endif + + RemoveUnusedReferences(mLocalize); + } + + #endregion + + #region GUI + + public override void OnInspectorGUI() + { + Undo.RecordObject(target, "Localize"); + + //GUI.backgroundColor = Color.Lerp (Color.black, Color.gray, 1); + //GUILayout.BeginVertical(GUIStyle_Background, GUILayout.Height(1)); + //GUI.backgroundColor = Color.white; + + if (GUILayout.Button("Localize", GUIStyle_Header)) + { + //Application.OpenURL(HelpURL_Documentation); + } + GUILayout.Space(-10); + + LocalizationManager.UpdateSources(); + + if (LocalizationManager.Sources.Count==0) + { + EditorGUILayout.HelpBox("Unable to find a Language Source.", MessageType.Warning); + } + else + { + GUILayout.Space(10); + OnGUI_Target (); + GUILayout.Space(10); + OnGUI_Terms(); + + //if (mGUI_ShowTems || mGUI_ShowReferences) GUILayout.Space(5); + + OnGUI_References(); + + if (mLocalize.mGUI_ShowReferences || mLocalize.mGUI_ShowCallback) GUILayout.Space(10); + + //Localize loc = target as Localize; + + //--[ Localize Callback ]---------------------- + EditorGUILayout.PropertyField(mProp_CallbackEvent, new GUIContent("On Localize Callback")); + + //string HeaderTitle = "On Localize Call:"; + //if (!mLocalize.mGUI_ShowCallback && loc.LocalizeCallBack.Target!=null && !string.IsNullOrEmpty(loc.LocalizeCallBack.MethodName)) + // HeaderTitle = string.Concat(HeaderTitle, " ",loc.LocalizeCallBack.Target.name, ".", loc.LocalizeCallBack.MethodName, ""); + //mLocalize.mGUI_ShowCallback = GUITools.DrawHeader(HeaderTitle, mLocalize.mGUI_ShowCallback); + //if (mLocalize.mGUI_ShowCallback) + //{ + // GUITools.BeginContents(); + // DrawEventCallBack( loc.LocalizeCallBack, loc ); + // GUITools.EndContents(); + //} + } + OnGUI_Source (); + + GUILayout.Space (10); + + GUITools.OnGUI_Footer("I2 Localization", LocalizationManager.GetVersion(), HelpURL_forum, HelpURL_Documentation, HelpURL_AssetStore); + + //GUILayout.EndVertical(); + + serializedObject.ApplyModifiedProperties(); + if (UnityEngine.Event.current.type == EventType.Repaint) + { + LocalizationEditor.mTestAction = LocalizationEditor.eTest_ActionType.None; + LocalizationEditor.mTestActionArg = null; + LocalizationEditor.mTestActionArg2 = null; + } + } + + #endregion + + #region References + + void OnGUI_References() + { + if (mLocalize.mGUI_ShowReferences = GUITools.DrawHeader ("References", mLocalize.mGUI_ShowReferences)) + { + GUITools.BeginContents(); + + bool canTest = UnityEngine.Event.current.type == EventType.Repaint; + + var testAddObj = canTest && LocalizationEditor.mTestAction == LocalizationEditor.eTest_ActionType.Button_Assets_Add ? (Object)LocalizationEditor.mTestActionArg : null; + var testReplaceIndx = canTest && LocalizationEditor.mTestAction == LocalizationEditor.eTest_ActionType.Button_Assets_Replace ? (int)LocalizationEditor.mTestActionArg : -1; + var testReplaceObj = canTest && LocalizationEditor.mTestAction == LocalizationEditor.eTest_ActionType.Button_Assets_Replace ? (Object)LocalizationEditor.mTestActionArg2 : null; + var testDeleteIndx = canTest && LocalizationEditor.mTestAction == LocalizationEditor.eTest_ActionType.Button_Assets_Delete ? (int)LocalizationEditor.mTestActionArg : -1; + + bool changed = GUITools.DrawObjectsArray( mProp_TranslatedObjects, false, false, true, testAddObj, testReplaceObj, testReplaceIndx, testDeleteIndx); + if (changed) + { + serializedObject.ApplyModifiedProperties(); + foreach (var obj in serializedObject.targetObjects) + (obj as Localize).UpdateAssetDictionary(); + } + + GUITools.EndContents(); + } + } + + void RemoveUnusedReferences(Localize cmp) + { + cmp.TranslatedObjects.RemoveAll(x => !IsUsingReference(LocalizationManager.GetTermData(cmp.Term), x) && !IsUsingReference(LocalizationManager.GetTermData(cmp.SecondaryTerm), x)); + if (cmp.TranslatedObjects.Count != cmp.mAssetDictionary.Count) + cmp.UpdateAssetDictionary(); + } + + bool IsUsingReference(TermData termData, Object obj ) + { + if (obj == null || termData==null) return false; + + string objName = obj.name; + foreach (string translation in termData.Languages) + { + if (translation != null && translation.Contains(objName)) + return true; + } + return false; + } + + + #endregion + + + #region Terms + + int GUI_SelectedTerm; + void OnGUI_Terms() + { + if ((mLocalize.mGUI_ShowTems=GUITools.DrawHeader ("Terms", mLocalize.mGUI_ShowTems))) + { + //--[ tabs: Main and Secondary Terms ]---------------- + int oldTab = GUI_SelectedTerm; + if (mLocalize.mLocalizeTarget!=null && mLocalize.mLocalizeTarget.CanUseSecondaryTerm()) + { + GUI_SelectedTerm = GUITools.DrawTabs (GUI_SelectedTerm, new[]{"Main", "Secondary"}); + } + else + { + GUI_SelectedTerm = 0; + GUITools.DrawTabs (GUI_SelectedTerm, new[]{"Main", ""}); + } + + GUITools.BeginContents(); + + TermData termData = null; + + if (GUI_SelectedTerm==0) termData = OnGUI_PrimaryTerm( oldTab!=GUI_SelectedTerm ); + else termData = OnGUI_SecondaryTerm(oldTab!=GUI_SelectedTerm); + + GUITools.EndContents(); + + //--[ Modifier ]------------- + if (mLocalize.Term != "-" && termData!=null && termData.TermType==eTermType.Text) + { + EditorGUI.BeginChangeCheck(); + GUILayout.BeginHorizontal(); + GUILayout.Label("Prefix:"); + EditorGUILayout.PropertyField(mProp_TermPrefix, GUITools.EmptyContent); + GUILayout.Label("Suffix:"); + EditorGUILayout.PropertyField(mProp_TermSuffix, GUITools.EmptyContent); + GUILayout.EndHorizontal(); + if (EditorGUI.EndChangeCheck()) + { + EditorApplication.delayCall += () => + { + if (targets != null) + { + foreach (var t in targets) + if (t as Localize != null) + (t as Localize).OnLocalize(true); + } + }; + } + EditorGUI.BeginChangeCheck(); + int val = EditorGUILayout.Popup("Modifier", GUI_SelectedTerm == 0 ? (int)mLocalize.PrimaryTermModifier : (int)mLocalize.SecondaryTermModifier, Enum.GetNames(typeof(Localize.TermModification))); + if (EditorGUI.EndChangeCheck()) + { + serializedObject.FindProperty(GUI_SelectedTerm == 0 ? "PrimaryTermModifier" : "SecondaryTermModifier").enumValueIndex = val; + GUI.changed = false; + } + } + + OnGUI_Options(); + //--[ OnAwake vs OnEnable ]------------- + //GUILayout.BeginHorizontal(); + //mProp_LocalizeOnAwake.boolValue = GUILayout.Toggle(mProp_LocalizeOnAwake.boolValue, new GUIContent(" Pre-Localize on Awake", "Localizing on Awake could result in a lag when the level is loaded but faster later when objects are enabled. If false, it will Localize OnEnable, so will yield faster level load but could have a lag when screens are enabled")); + //GUILayout.FlexibleSpace(); + //if (mLocalize.HasCallback()) + //{ + // GUI.enabled = false; + // GUILayout.Toggle(true, new GUIContent(" Force Localize", "Enable this when the translations have parameters (e.g. Thew winner is {[WINNER}]) to prevent any optimization that could prevent updating the translation when the object is enabled")); + // GUI.enabled = true; + //} + //else + //{ + // mProp_AlwaysForceLocalize.boolValue = GUILayout.Toggle(mProp_AlwaysForceLocalize.boolValue, new GUIContent(" Force Localize", "Enable this when the translations have parameters (e.g. Thew winner is {[WINNER}]) to prevent any optimization that could prevent updating the translation when the object is enabled")); + //} + //GUILayout.EndHorizontal(); + + //--[ Right To Left ]------------- + if (!mLocalize.IgnoreRTL && mLocalize.Term!="-" && termData != null && termData.TermType == eTermType.Text) + { + GUILayout.BeginVertical("Box"); + //GUILayout.BeginHorizontal(); + // mProp_IgnoreRTL.boolValue = GUILayout.Toggle(mProp_IgnoreRTL.boolValue, new GUIContent(" Ignore Right To Left", "Arabic and other RTL languages require processing them so they render correctly, this toogle allows ignoring that processing (in case you are doing it manually during a callback)")); + // GUILayout.FlexibleSpace(); + // mProp_SeparateWords.boolValue = GUILayout.Toggle(mProp_SeparateWords.boolValue, new GUIContent(" Separate Words", " Some languages (e.g. Chinese, Japanese and Thai) don't add spaces to their words (all characters are placed toguether), enabling this checkbox, will add spaces to all characters to allow wrapping long texts into multiple lines.")); + //GUILayout.EndHorizontal(); + { + mProp_MaxCharactersInRTL.intValue = EditorGUILayout.IntField( new GUIContent("Max line length", "If the language is Right To Left, long lines will be split at this length and the RTL fix will be applied to each line, this should be set to the maximum number of characters that fit in this text width. 0 disables the per line fix"), mProp_MaxCharactersInRTL.intValue ); + GUILayout.BeginHorizontal(); + mProp_CorrectAlignmentForRTL.boolValue = GUILayout.Toggle(mProp_CorrectAlignmentForRTL.boolValue, new GUIContent(" Adjust Alignment", "Right-align when Right-To-Left Language, and Left-Align otherwise") ); + GUILayout.FlexibleSpace(); + mProp_IgnoreNumbersInRTL.boolValue = GUILayout.Toggle(mProp_IgnoreNumbersInRTL.boolValue, new GUIContent(" Ignore Numbers", "Preserve numbers as latin characters instead of converting them")); + GUILayout.EndHorizontal(); + } + + GUILayout.EndVertical(); + } + + + ////GUILayout.EndHorizontal(); + } + } + + void OnGUI_Options() + { + int mask = 0; + if (mProp_LocalizeOnAwake.boolValue) mask |= 1 << 0; + if (mProp_AlwaysForceLocalize.boolValue) mask |= 1 << 1; + if (mProp_AllowParameters.boolValue) mask |= 1 << 2; + if (mProp_AllowLocalizedParameters.boolValue) mask |= 1 << 3; + if (mProp_SeparateWords.boolValue) mask |= 1 << 4; + if (mProp_IgnoreRTL.boolValue) mask |= 1 << 5; + + EditorGUI.BeginChangeCheck(); + mask = EditorGUILayout.MaskField(new GUIContent("Options"), mask, new []{ + "Localize On Awake", + "Force Localize", + "Allow Parameters", + "Allow Localized Parameters", + "Separate Words", + "Ignore RTL" + }); + if (EditorGUI.EndChangeCheck()) + { + mProp_LocalizeOnAwake.boolValue = (mask & (1 << 0))> 0; + mProp_AlwaysForceLocalize.boolValue = (mask & (1 << 1))> 0; + mProp_AllowParameters.boolValue = (mask & (1 << 2))> 0; + mProp_AllowLocalizedParameters.boolValue = (mask & (1 << 3))> 0; + mProp_SeparateWords.boolValue = (mask & (1 << 4))> 0; + mProp_IgnoreRTL.boolValue = (mask & (1 << 5))> 0; + } + } + + TermData OnGUI_PrimaryTerm( bool OnOpen ) + { + string Key = mLocalize.mTerm; + if (string.IsNullOrEmpty(Key)) + { + string SecondaryTerm; + mLocalize.GetFinalTerms( out Key, out SecondaryTerm ); + } + + if (OnOpen) mNewKeyName = Key; + if ( OnGUI_SelectKey( ref Key, string.IsNullOrEmpty(mLocalize.mTerm))) + mProp_mTerm.stringValue = Key; + return LocalizationEditor.OnGUI_Keys_Languages( Key, mLocalize ); + } + + TermData OnGUI_SecondaryTerm( bool OnOpen ) + { + string Key = mLocalize.mTermSecondary; + + if (string.IsNullOrEmpty(Key)) + { + string ss; + mLocalize.GetFinalTerms( out ss, out Key ); + } + + if (OnOpen) mNewKeyName = Key; + if ( OnGUI_SelectKey( ref Key, string.IsNullOrEmpty(mLocalize.mTermSecondary))) + mProp_mTermSecondary.stringValue = Key; + return LocalizationEditor.OnGUI_Keys_Languages( Key, mLocalize, false ); + } + + bool OnGUI_SelectKey( ref string Term, bool Inherited ) // Inherited==true means that the mTerm is empty and we are using the Label.text instead + { + GUILayout.Space (5); + GUILayout.BeginHorizontal(); + + GUI.changed = false; + mAllowEditKeyName = GUILayout.Toggle(mAllowEditKeyName, "Term:", EditorStyles.foldout, GUILayout.ExpandWidth(false)); + if (GUI.changed && mAllowEditKeyName) { + mNewKeyName = Term; + mTermsArray = null; + } + + bool bChanged = false; + + if (mTermsArray==null || Term!="-" && Array.IndexOf(mTermsArray, Term)<0) + UpdateTermsList(Term); + + if (Inherited) + GUI.contentColor = Color.Lerp (Color.gray, Color.yellow, 0.1f); + + int Index = Term=="-" || Term=="" ? mTermsArray.Length-1 : Array.IndexOf( mTermsArray, Term ); + + GUI.changed = false; + + int newIndex = EditorGUILayout.Popup( Index, mTermsArray); + + GUI.contentColor = Color.white; + if (/*newIndex != Index && newIndex>=0*/GUI.changed) + { + GUI.changed = false; + if (mLocalize.Source != null && newIndex == mTermsArray.Length - 4) //< show terms from all sources > + { + mLocalize.Source = null; + mTermsArray = null; + } + else + if (newIndex == mTermsArray.Length - 2) // + mNewKeyName = Term = string.Empty; + else + if (newIndex == mTermsArray.Length - 1) // + mNewKeyName = Term = "-"; + else + mNewKeyName = Term = mTermsArray[newIndex]; + + + if (GUI_SelectedTerm==0) + mLocalize.SetTerm (mNewKeyName); + else + mLocalize.SetTerm (null, mNewKeyName); + mAllowEditKeyName = false; + bChanged = true; + } + + LanguageSourceData source = LocalizationManager.GetSourceContaining(Term); + TermData termData = source.GetTermData(Term); + if (termData!=null) + { + if (Inherited) + bChanged = true; // if the term its inferred and a matching term its found, then use that + eTermType NewType = (eTermType)EditorGUILayout.EnumPopup(termData.TermType, GUILayout.Width(90)); + if (termData.TermType != NewType) + termData.TermType = NewType; + } + + GUILayout.EndHorizontal(); + + if (mAllowEditKeyName) + { + GUILayout.BeginHorizontal(GUILayout.Height (1)); + GUILayout.BeginHorizontal(EditorStyles.toolbar); + if(mNewKeyName==null) mNewKeyName = string.Empty; + + GUI.changed = false; + mNewKeyName = EditorGUILayout.TextField(mNewKeyName, new GUIStyle(GUITools.Style_ToolbarSearchTextField), GUILayout.ExpandWidth(true)); + if (GUI.changed) + { + mTermsArray = null; // regenerate this array to apply filtering + GUI.changed = false; + } + + if (GUILayout.Button (string.Empty, string.IsNullOrEmpty(mNewKeyName) ? GUITools.Style_ToolbarSearchCancelButtonEmpty : GUITools.Style_ToolbarSearchCancelButton, GUILayout.ExpandWidth(false))) + { + mTermsArray = null; // regenerate this array to apply filtering + mNewKeyName = string.Empty; + } + + GUILayout.EndHorizontal(); + + string ValidatedName = mNewKeyName; + LanguageSourceData.ValidateFullTerm( ref ValidatedName ); + + bool CanUseNewName = source.GetTermData(ValidatedName)==null; + GUI.enabled = !string.IsNullOrEmpty(mNewKeyName) && CanUseNewName; + if (GUILayout.Button ("Create", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) + { + mNewKeyName = ValidatedName; + mTermsArray=null; // this recreates that terms array + + LanguageSourceData Source = null; + #if UNITY_EDITOR + if (mLocalize.Source!=null) + Source = mLocalize.Source.SourceData; + #endif + + if (Source==null) + Source = LocalizationManager.Sources[0]; + Term = mNewKeyName; + var data = Source.AddTerm( mNewKeyName, eTermType.Text, false ); + if (data.Languages.Length > 0) + data.Languages[0] = mLocalize.GetMainTargetsText(); + Source.Editor_SetDirty(); + AssetDatabase.SaveAssets(); + mAllowEditKeyName = false; + bChanged = true; + GUIUtility.keyboardControl = 0; + } + GUI.enabled = termData!=null && !string.IsNullOrEmpty(mNewKeyName) && CanUseNewName; + if (GUILayout.Button (new GUIContent("Rename","Renames the term in the source and updates every object using it in the current scene"), EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) + { + mNewKeyName = ValidatedName; + Term = mNewKeyName; + mTermsArray=null; // this recreates that terms array + mAllowEditKeyName = false; + bChanged = true; + LocalizationEditor.TermReplacements = new Dictionary(StringComparer.Ordinal); + LocalizationEditor.TermReplacements[ termData.Term ] = mNewKeyName; + termData.Term = mNewKeyName; + source.UpdateDictionary(true); + LocalizationEditor.ReplaceTermsInCurrentScene(); + GUIUtility.keyboardControl = 0; + EditorApplication.update += LocalizationEditor.DoParseTermsInCurrentScene; + } + GUI.enabled = true; + GUILayout.EndHorizontal(); + + bChanged |= OnGUI_SelectKey_PreviewTerms ( ref Term); + } + + GUILayout.Space (5); + return bChanged; + } + + void UpdateTermsList( string currentTerm ) + { + List Terms = mLocalize.Source==null ? LocalizationManager.GetTermsList() : mLocalize.Source.SourceData.GetTermsList(); + + // If there is a filter, remove all terms not matching that filter + if (mAllowEditKeyName && !string.IsNullOrEmpty(mNewKeyName)) + { + string Filter = mNewKeyName.ToUpper(); + for (int i=Terms.Count-1; i>=0; --i) + if (!Terms[i].ToUpper().Contains(Filter) && Terms[i]!=currentTerm) + Terms.RemoveAt(i); + + } + + if (!string.IsNullOrEmpty(currentTerm) && currentTerm!="-" && !Terms.Contains(currentTerm)) + Terms.Add (currentTerm); + + Terms.Sort(StringComparer.OrdinalIgnoreCase); + Terms.Add(""); + if (mLocalize.Source != null) + { + Terms.Add("< Show Terms from all sources >"); + Terms.Add(""); + } + Terms.Add(""); + Terms.Add(""); + + mTermsArray = Terms.ToArray(); + } + + bool OnGUI_SelectKey_PreviewTerms ( ref string Term) + { + if (mTermsArray==null) + UpdateTermsList(Term); + + int nTerms = mTermsArray.Length; + if (nTerms<=0) + return false; + + if (nTerms==1 && mTermsArray[0]==Term) + return false; + + bool bChanged = false; + GUI.backgroundColor = Color.gray; + GUILayout.BeginVertical (GUIStyle_OldTextArea); + for (int i = 0, imax = Mathf.Min (nTerms, 3); i < imax; ++i) + { + ParsedTerm parsedTerm; + int nUses = -1; + if (LocalizationEditor.mParsedTerms.TryGetValue (mTermsArray [i], out parsedTerm)) + nUses = parsedTerm.Usage; + + string FoundText = mTermsArray [i]; + if (nUses > 0) + FoundText = string.Concat ("(", nUses, ") ", FoundText); + + if (GUILayout.Button (FoundText, EditorStyles.miniLabel, GUILayout.MaxWidth(EditorGUIUtility.currentViewWidth - 70))) + { + if (mTermsArray[i] == "") + mNewKeyName = Term = string.Empty; + else + if (mTermsArray[i] == "") + mNewKeyName = Term = "-"; + else + if (mTermsArray[i] != "< Show Terms from all sources >") + mNewKeyName = Term = mTermsArray[i]; + + //mNewKeyName = Term = (mTermsArray [i]=="" ? string.Empty : mTermsArray [i]); + GUIUtility.keyboardControl = 0; + mAllowEditKeyName = false; + bChanged = true; + } + } + if (nTerms > 3) + GUILayout.Label ("..."); + GUILayout.EndVertical (); + GUI.backgroundColor = Color.white; + + return bChanged; + } + + #endregion + + #region Target + + void OnGUI_Target() + { + List TargetTypes = new List(); + int CurrentTarget = -1; + + mLocalize.FindTarget(); + + foreach (var desc in LocalizationManager.mLocalizeTargets) + { + if (desc.CanLocalize(mLocalize)) + { + TargetTypes.Add(desc.Name); + + if (mLocalize.mLocalizeTarget!=null && desc.GetTargetType() == mLocalize.mLocalizeTarget.GetType()) + CurrentTarget = TargetTypes.Count - 1; + } + } + + if (CurrentTarget==-1) + { + CurrentTarget = TargetTypes.Count; + TargetTypes.Add("None"); + } + + GUILayout.BeginHorizontal(); + GUILayout.Label ("Target:", GUILayout.Width (60)); + EditorGUI.BeginChangeCheck(); + int index = EditorGUILayout.Popup(CurrentTarget, TargetTypes.ToArray()); + if (EditorGUI.EndChangeCheck()) + { + serializedObject.ApplyModifiedProperties(); + foreach (var obj in serializedObject.targetObjects) + { + var cmp = obj as Localize; + if (cmp == null) + continue; + + if (cmp.mLocalizeTarget != null) + DestroyImmediate(cmp.mLocalizeTarget); + cmp.mLocalizeTarget = null; + + foreach (var desc in LocalizationManager.mLocalizeTargets) + { + if (desc.Name == TargetTypes[index]) + { + cmp.mLocalizeTarget = desc.CreateTarget(cmp); + cmp.mLocalizeTargetName = desc.GetTargetType().ToString(); + break; + } + } + } + serializedObject.Update(); + } + GUILayout.EndHorizontal(); + } + + #endregion + + #region Source + + void OnGUI_Source() + { + GUILayout.BeginHorizontal(); + + ILanguageSource currentSource = mLocalize.Source; + if (currentSource==null) + { + LanguageSourceData source = LocalizationManager.GetSourceContaining(mLocalize.Term); + currentSource = source==null ? null : source.owner; + } + + if (GUILayout.Button("Open Source", EditorStyles.toolbarButton, GUILayout.Width (100))) + { + Selection.activeObject = currentSource as Object; + + string sTerm, sSecondary; + mLocalize.GetFinalTerms( out sTerm, out sSecondary ); + if (GUI_SelectedTerm==1) sTerm = sSecondary; + LocalizationEditor.mKeyToExplore = sTerm; + } + + GUILayout.Space (2); + + GUILayout.BeginHorizontal(EditorStyles.toolbar); + EditorGUI.BeginChangeCheck (); + if (mLocalize.Source == null) + { + GUI.contentColor = Color.Lerp (Color.gray, Color.yellow, 0.1f); + } + Object obj = EditorGUILayout.ObjectField(currentSource as Object, typeof(Object), true); + GUI.contentColor = Color.white; + if (EditorGUI.EndChangeCheck()) + { + ILanguageSource NewSource = obj as ILanguageSource; + if (NewSource == null && obj as GameObject != null) + { + NewSource = (obj as GameObject).GetComponent(); + } + + mLocalize.Source = NewSource; + string sTerm, sSecondary; + mLocalize.GetFinalTerms(out sTerm, out sSecondary); + if (GUI_SelectedTerm == 1) sTerm = sSecondary; + UpdateTermsList(sTerm); + } + + if (GUILayout.Button(new GUIContent("Detect", "Finds the LanguageSource containing the selected term, the term list will now only show terms inside that source."), EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) + { + string sTerm, sSecondary; + mLocalize.GetFinalTerms(out sTerm, out sSecondary); + if (GUI_SelectedTerm == 1) sTerm = sSecondary; + + var data = LocalizationManager.GetSourceContaining(sTerm, false); + mLocalize.Source = data==null ? null : data.owner; + mTermsArray = null; + } + GUILayout.EndHorizontal(); + + GUILayout.EndHorizontal(); + } + + #endregion + + + #region Event CallBack + + //public void DrawEventCallBack( EventCallback CallBack, Localize loc ) + //{ + //if (CallBack==null) + // return; + + //GUI.changed = false; + + //GUILayout.BeginHorizontal(); + //GUILayout.Label("Target:", GUILayout.ExpandWidth(false)); + //CallBack.Target = EditorGUILayout.ObjectField( CallBack.Target, typeof(MonoBehaviour), true) as MonoBehaviour; + //GUILayout.EndHorizontal(); + + //if (CallBack.Target!=null) + //{ + // GameObject GO = CallBack.Target.gameObject; + // List Infos = new List(); + + // var targets = GO.GetComponents(typeof(MonoBehaviour)); + // foreach (var behavior in targets) + // Infos.AddRange( behavior.GetType().GetMethods() ); + + // List Methods = new List(); + + // for (int i = 0, imax=Infos.Count; i 1) return false; + + if (Params [0].ParameterType.IsSubclassOf (typeof(Object))) return true; + if (Params [0].ParameterType == typeof(Object)) return true; + return false; + } + + + #endregion + + #region Styles + + public static GUIStyle GUIStyle_Header { + get{ + if (mGUIStyle_Header==null) + { + mGUIStyle_Header = new GUIStyle("HeaderLabel"); + mGUIStyle_Header.fontSize = 25; + mGUIStyle_Header.normal.textColor = Color.Lerp(Color.white, Color.gray, 0.5f); + mGUIStyle_Header.fontStyle = FontStyle.BoldAndItalic; + mGUIStyle_Header.alignment = TextAnchor.UpperCenter; + } + return mGUIStyle_Header; + } + } + static GUIStyle mGUIStyle_Header; + + public static GUIStyle GUIStyle_SubHeader { + get{ + if (mGUIStyle_SubHeader==null) + { + mGUIStyle_SubHeader = new GUIStyle("HeaderLabel"); + mGUIStyle_SubHeader.fontSize = 13; + mGUIStyle_SubHeader.fontStyle = FontStyle.Normal; + mGUIStyle_SubHeader.margin.top = -50; + mGUIStyle_SubHeader.alignment = TextAnchor.UpperCenter; + } + return mGUIStyle_SubHeader; + } + } + static GUIStyle mGUIStyle_SubHeader; + + public static GUIStyle GUIStyle_Background { + get{ + if (mGUIStyle_Background==null) + { + mGUIStyle_Background = new GUIStyle(EditorStyles.textArea); + mGUIStyle_Background.fixedHeight = 0; + mGUIStyle_Background.overflow.left = 50; + mGUIStyle_Background.overflow.right = 50; + mGUIStyle_Background.overflow.top = -5; + mGUIStyle_Background.overflow.bottom = 0; + } + return mGUIStyle_Background; + } + } + static GUIStyle mGUIStyle_Background; + + public static GUIStyle GUIStyle_OldTextArea + { + get + { + if (mGUIStyle_OldTextArea == null) + { + mGUIStyle_OldTextArea = new GUIStyle(EditorStyles.textArea); + mGUIStyle_OldTextArea.fixedHeight = 0; + } + return mGUIStyle_OldTextArea; + } + } + static GUIStyle mGUIStyle_OldTextArea; + + #endregion + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizeInspector.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizeInspector.cs.meta new file mode 100644 index 00000000..6d7f3aad --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/LocalizeInspector.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 675119279b2a30245801272112cfbe38 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/ResourceManagerInspector.cs b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/ResourceManagerInspector.cs new file mode 100644 index 00000000..692c70bd --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/ResourceManagerInspector.cs @@ -0,0 +1,29 @@ +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + [CustomEditor(typeof(ResourceManager))] + public class ResourceManagerInspector : UnityEditor.Editor + { + SerializedProperty mAssets; + + void OnEnable() + { + UpgradeManager.EnablePlugins(); + mAssets = serializedObject.FindProperty("Assets"); + } + + public override void OnInspectorGUI() + { + GUILayout.Space(5); + GUITools.DrawHeader("Assets:", true); + GUITools.BeginContents(); + ///GUILayout.Label ("Assets:"); + GUITools.DrawObjectsArray( mAssets ); + GUITools.EndContents(); + + serializedObject.ApplyModifiedProperties(); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/ResourceManagerInspector.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/ResourceManagerInspector.cs.meta new file mode 100644 index 00000000..96076a94 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/ResourceManagerInspector.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: ba2fdf8face79dd4f9e1ed80448db843 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/SetLanguageInspector.cs b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/SetLanguageInspector.cs new file mode 100644 index 00000000..17a5e4e9 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/SetLanguageInspector.cs @@ -0,0 +1,60 @@ +using System; +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + [CustomEditor(typeof(SetLanguage))] + public class SetLanguageInspector : UnityEditor.Editor + { + public SetLanguage setLan; + public SerializedProperty mProp_Language; + + public void OnEnable() + { + setLan = (SetLanguage)target; + mProp_Language = serializedObject.FindProperty("_Language"); + } + + public override void OnInspectorGUI() + { + string[] Languages; + LanguageSource sourceObj = setLan.mSource; + if (sourceObj == null) + { + LocalizationManager.UpdateSources(); + Languages = LocalizationManager.GetAllLanguages().ToArray(); + Array.Sort(Languages); + } + else + { + Languages = sourceObj.mSource.GetLanguages().ToArray(); + Array.Sort(Languages); + } + + int index = Array.IndexOf(Languages, mProp_Language.stringValue); + + GUI.changed = false; + index = EditorGUILayout.Popup("Language", index, Languages); + if (GUI.changed) + { + if (index<0 || index>=Languages.Length) + mProp_Language.stringValue = string.Empty; + else + mProp_Language.stringValue = Languages[index]; + GUI.changed = false; + serializedObject.ApplyModifiedProperties(); + } + + GUILayout.Space(5); + if (setLan.mSource==null) GUI.contentColor = Color.Lerp (Color.gray, Color.yellow, 0.1f); + sourceObj = EditorGUILayout.ObjectField("Language Source:", sourceObj, typeof(LanguageSource), true) as LanguageSource; + GUI.contentColor = Color.white; + + if (GUI.changed) + setLan.mSource = sourceObj; + + serializedObject.ApplyModifiedProperties(); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/SetLanguageInspector.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/SetLanguageInspector.cs.meta new file mode 100644 index 00000000..8fb138e1 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/SetLanguageInspector.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 7af58b4da44670e47a509c59754e8c2b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/TermsPopup_Drawer.cs b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/TermsPopup_Drawer.cs new file mode 100644 index 00000000..05e17645 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/TermsPopup_Drawer.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + [CustomPropertyDrawer (typeof (TermsPopup))] + public class TermsPopup_Drawer : PropertyDrawer + { + GUIContent[] mTerms_Context; + int nFramesLeftBeforeUpdate; + string mPrevFilter; + + public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) + { + var filter = ((TermsPopup)attribute).Filter; + ShowGUICached(position, property, label, null, filter, ref mTerms_Context, ref nFramesLeftBeforeUpdate, ref mPrevFilter); + } + + public static bool ShowGUI(Rect position, SerializedProperty property, GUIContent label, LanguageSourceData source, string filter = "") + { + GUIContent[] terms=null; + int framesLeftBeforeUpdate=0; + string prevFilter = null; + + return ShowGUICached(position, property, label, source, filter, ref terms, ref framesLeftBeforeUpdate, ref prevFilter); + } + + public static bool ShowGUICached(Rect position, SerializedProperty property, GUIContent label, LanguageSourceData source, string filter, ref GUIContent[] terms_Contexts, ref int framesBeforeUpdating, ref string prevFilter) + { + UpdateTermsCache(source, filter, ref terms_Contexts, ref framesBeforeUpdating, ref prevFilter); + + label = EditorGUI.BeginProperty(position, label, property); + + EditorGUI.BeginChangeCheck (); + + var index = property.stringValue == "-" || property.stringValue == "" ? terms_Contexts.Length - 1 : + property.stringValue == " " ? terms_Contexts.Length - 2 : + GetTermIndex(terms_Contexts, property.stringValue); + var newIndex = EditorGUI.Popup(position, label, index, terms_Contexts); + + if (EditorGUI.EndChangeCheck()) + { + property.stringValue = newIndex < 0 || newIndex == terms_Contexts.Length - 1 ? string.Empty : terms_Contexts[newIndex].text; + if (newIndex == terms_Contexts.Length - 1) + property.stringValue = "-"; + else + if (newIndex < 0 || newIndex == terms_Contexts.Length - 2) + property.stringValue = string.Empty; + else + property.stringValue = terms_Contexts[newIndex].text; + + EditorGUI.EndProperty(); + return true; + } + + EditorGUI.EndProperty(); + return false; + } + + static int GetTermIndex(GUIContent[] terms_Contexts, string term ) + { + for (int i = 0; i < terms_Contexts.Length; ++i) + if (terms_Contexts[i].text == term) + return i; + return -1; + } + + + static void UpdateTermsCache(LanguageSourceData source, string filter, ref GUIContent[] terms_Contexts, ref int framesBeforeUpdating, ref string prevFilter) + { + framesBeforeUpdating--; + if (terms_Contexts!=null && framesBeforeUpdating>0 && filter==prevFilter) + { + return; + } + framesBeforeUpdating = 60; + prevFilter = filter; + + var Terms = source == null ? LocalizationManager.GetTermsList() : source.GetTermsList(); + + if (string.IsNullOrEmpty(filter) == false) + { + Terms = Filter(Terms, filter); + } + + Terms.Sort(StringComparer.OrdinalIgnoreCase); + Terms.Add(""); + Terms.Add(""); + Terms.Add(""); + + terms_Contexts = DisplayOptions(Terms); + } + + private static List Filter(List terms, string filter) + { + var filtered = new List(); + for (var i = 0; i < terms.Count; i++) + { + var term = terms[i]; + if (term.Contains(filter)) + { + filtered.Add(term); + } + } + + return filtered; + } + + private static GUIContent[] DisplayOptions(IList terms) + { + var options = new GUIContent[terms.Count]; + for (var i = 0; i < terms.Count; i++) + { + options[i] = new GUIContent(terms[i]); + } + + return options; + } + } + + [CustomPropertyDrawer(typeof(LocalizedString))] + public class LocalizedStringDrawer : PropertyDrawer + { + GUIContent[] mTerms_Context; + int nFramesLeftBeforeUpdate; + string mPrevFilter; + + public override void OnGUI(Rect rect, SerializedProperty property, GUIContent label) + { + var termRect = rect; termRect.xMax -= 50; + var termProp = property.FindPropertyRelative("mTerm"); + TermsPopup_Drawer.ShowGUICached(termRect, termProp, label, null, "", ref mTerms_Context, ref nFramesLeftBeforeUpdate, ref mPrevFilter); + + var maskRect = rect; maskRect.xMin = maskRect.xMax - 30; + var termIgnoreRTL = property.FindPropertyRelative("mRTL_IgnoreArabicFix"); + var termConvertNumbers = property.FindPropertyRelative("mRTL_ConvertNumbers"); + var termDontLocalizeParams = property.FindPropertyRelative("m_DontLocalizeParameters"); + int mask = (termIgnoreRTL.boolValue ? 0 : 1) + + (termConvertNumbers.boolValue ? 0 : 2) + + (termDontLocalizeParams.boolValue ? 0 : 4); + + int newMask = EditorGUI.MaskField(maskRect, mask, new[] { "Arabic Fix", "Ignore Numbers in RTL", "Localize Parameters" }); + if (newMask != mask) + { + termIgnoreRTL.boolValue = (newMask & 1) == 0; + termConvertNumbers.boolValue = (newMask & 2) == 0; + termDontLocalizeParams.boolValue = (newMask & 4) == 0; + } + + var showRect = rect; showRect.xMin = termRect.xMax; showRect.xMax=maskRect.xMin; + bool enabled = GUI.enabled; + GUI.enabled = enabled & (!string.IsNullOrEmpty (termProp.stringValue) && termProp.stringValue!="-"); + if (GUI.Button (showRect, "?")) + { + var source = LocalizationManager.GetSourceContaining(termProp.stringValue); + LocalizationEditor.mKeyToExplore = termProp.stringValue; + Selection.activeObject = source.ownerObject; + } + GUI.enabled = enabled; + } + } +} diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/TermsPopup_Drawer.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/TermsPopup_Drawer.cs.meta new file mode 100644 index 00000000..0290190a --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Inspectors/TermsPopup_Drawer.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 51c22a426b92fa84cb6ca7b75176da8a +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization.meta b/UnityProject/Assets/TEngine/Editor/Localization/Localization.meta new file mode 100644 index 00000000..f169efc7 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 8ad136296e8e6e14eaa2726ac1992b6c +folderAsset: yes +timeCreated: 1461137613 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor.cs b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor.cs new file mode 100644 index 00000000..1fecf387 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor.cs @@ -0,0 +1,308 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace TEngine.Localization +{ + public partial class LocalizationEditor + { + + #region Variables + + public enum eViewMode { ImportExport, Keys, Languages, Tools, References } + public static eViewMode mCurrentViewMode = eViewMode.Keys; + + public enum eSpreadsheetMode { Local, Google } + public eSpreadsheetMode mSpreadsheetMode = eSpreadsheetMode.Google; + + + public static string mLocalizationMsg = ""; + public static MessageType mLocalizationMessageType = MessageType.None; + + // These variables are for executing action from Unity Tests + public enum eTest_ActionType { None, Button_AddLanguageFromPopup, Button_AddLanguageManual, + Button_AddTerm_InTermsList, Button_AddSelectedTerms, + Button_RemoveSelectedTerms, Button_DeleteTerm, + Button_SelectTerms_All, Button_SelectTerms_None, Button_SelectTerms_Used, Button_SelectTerms_Missing, + Button_Term_Translate, Button_Term_TranslateAll, Button_Languages_TranslateAll, + Button_Assets_Add, Button_Assets_Replace, Button_Assets_Delete, + Button_GoogleSpreadsheet_RefreshList, Button_GoogleSpreadsheet_Export, Button_GoogleSpreadsheet_Import + } + public static eTest_ActionType mTestAction = eTest_ActionType.None; + public static object mTestActionArg, mTestActionArg2; + + #endregion + + #region Editor + + /*[MenuItem("Window/Localization", false)] + public static void OpenLocalizationEditor() + { + EditorWindow.GetWindow(false, "Localization", true); + }*/ + + #endregion + + #region GUI + + void InitializeStyles() + { + Style_ToolBar_Big = new GUIStyle(EditorStyles.toolbar); + Style_ToolBar_Big.fixedHeight = Style_ToolBar_Big.fixedHeight*1.5f; + + Style_ToolBarButton_Big = new GUIStyle(EditorStyles.toolbarButton); + Style_ToolBarButton_Big.fixedHeight = Style_ToolBarButton_Big.fixedHeight*1.5f; + } + + + void OnGUI_Main() + { + OnGUI_Warning_SourceInScene(); + OnGUI_Warning_SourceInsidePluginsFolder(); + OnGUI_Warning_SourceNotUpToDate(); + + var prevViewMode = mCurrentViewMode; + + GUILayout.BeginHorizontal(); + //OnGUI_ToggleEnumBig( "Spreadsheets", ref mCurrentViewMode, eViewMode.ImportExport, GUI.skin.GetStyle("CN EntryWarn").normal.background, "External Spreadsheet File or Service" ); + OnGUI_ToggleEnumBig( "Spreadsheets", ref mCurrentViewMode, eViewMode.ImportExport, null, "External Spreadsheet File or Service" ); + OnGUI_ToggleEnumBig( "Terms", ref mCurrentViewMode, eViewMode.Keys, null, null ); + OnGUI_ToggleEnumBig( "Languages", ref mCurrentViewMode, eViewMode.Languages, null, null ); + OnGUI_ToggleEnumBig( "Tools", ref mCurrentViewMode, eViewMode.Tools, null, null ); + OnGUI_ToggleEnumBig( "Assets", ref mCurrentViewMode, eViewMode.References, null, null ); + GUILayout.EndHorizontal(); + //GUILayout.Space(10); + + switch (mCurrentViewMode) + { + case eViewMode.ImportExport : OnGUI_ImportExport(); break; + case eViewMode.Keys : OnGUI_KeysList(); break; + case eViewMode.Languages : OnGUI_Languages(); break; + case eViewMode.Tools : OnGUI_Tools(prevViewMode != mCurrentViewMode); break; + case eViewMode.References : OnGUI_References(); break; + } + } + + void OnGUI_ImportExport() + { + eSpreadsheetMode OldMode = mSpreadsheetMode; + mSpreadsheetMode = (eSpreadsheetMode)GUITools.DrawShadowedTabs ((int)mSpreadsheetMode, new[]{"Local", "Google"}); + if (mSpreadsheetMode != OldMode) + ClearErrors(); + + GUITools.BeginContents(); + switch (mSpreadsheetMode) + { + case eSpreadsheetMode.Local : OnGUI_Spreadsheet_Local(); break; + case eSpreadsheetMode.Google : OnGUI_Spreadsheet_Google(); break; + } + GUITools.EndContents(false); + } + + void OnGUI_References() + { + EditorGUILayout.HelpBox("These are the assets that are referenced by the Terms and not in the Resources folder", MessageType.Info); + + bool canTest = UnityEngine.Event.current.type == EventType.Repaint; + + var testAddObj = canTest && mTestAction == eTest_ActionType.Button_Assets_Add ? (Object)mTestActionArg : null; + var testReplaceIndx = canTest && mTestAction == eTest_ActionType.Button_Assets_Replace ? (int)mTestActionArg : -1; + var testReplaceObj = canTest && mTestAction == eTest_ActionType.Button_Assets_Replace ? (Object)mTestActionArg2 : null; + var testDeleteIndx = canTest && mTestAction == eTest_ActionType.Button_Assets_Delete ? (int)mTestActionArg : -1; + + bool changed = GUITools.DrawObjectsArray( mProp_Assets, false, false, false, testAddObj, testReplaceObj, testReplaceIndx, testDeleteIndx); + if (changed) + { + serializedObject.ApplyModifiedProperties(); + foreach (var obj in serializedObject.targetObjects) + (obj as LanguageSource).mSource.UpdateAssetDictionary(); + } + } + + #endregion + + #region Misc + + void OnGUI_ToggleEnumBig( string text, ref Enum currentMode, Enum newMode, Texture texture, string tooltip) { OnGUI_ToggleEnum( text, ref currentMode, newMode, texture, tooltip, Style_ToolBarButton_Big); } + void OnGUI_ToggleEnumSmall( string text, ref Enum currentMode, Enum newMode, Texture texture, string tooltip) { OnGUI_ToggleEnum( text, ref currentMode, newMode, texture, tooltip, EditorStyles.toolbarButton); } + void OnGUI_ToggleEnum( string text, ref Enum currentMode, Enum newMode, Texture texture, string tooltip, GUIStyle style) + { + GUI.changed = false; + if (GUILayout.Toggle( currentMode.Equals(newMode), new GUIContent(text, texture, tooltip), style, GUILayout.ExpandWidth(true))) + { + currentMode = newMode; + if (GUI.changed) + ClearErrors(); + } + } + + int OnGUI_FlagToogle( string Text, string tooltip, int flags, int bit ) + { + bool State = (flags & bit)>0; + bool NewState = GUILayout.Toggle(State, new GUIContent(Text, tooltip), "toolbarbutton"); + if (State!=NewState) + { + if (!NewState && flags==bit) + return flags; + + flags = NewState ? flags | bit : flags & ~bit; + } + + return flags; + } + + void OnGUI_SelectableToogleListItem( string Element, ref List Selections, string Style ) + { + bool WasEnabled = Selections.Contains(Element); + bool IsEnabled = GUILayout.Toggle( WasEnabled, "", Style, GUILayout.ExpandWidth(false) ); + + if (IsEnabled && !WasEnabled) + Selections.Add(Element); + else + if (!IsEnabled && WasEnabled) + Selections.Remove(Element); + } + + void OnGUI_SelectableToogleListItem( Rect rect, string Element, ref List Selections, string Style ) + { + bool WasEnabled = Selections.Contains(Element); + bool IsEnabled = GUI.Toggle( rect, WasEnabled, "", Style ); + + if (IsEnabled && !WasEnabled) + Selections.Add(Element); + else + if (!IsEnabled && WasEnabled) + Selections.Remove(Element); + } + + static bool InTestAction( eTest_ActionType testType ) + { + return mTestAction == testType && UnityEngine.Event.current.type == EventType.Repaint; + } + static bool TestButton(eTest_ActionType action, string text, GUIStyle style, params GUILayoutOption[] options) + { + return GUILayout.Button(text, style, options) || mTestAction == action && UnityEngine.Event.current.type == EventType.Repaint; + } + + static bool TestButtonArg(eTest_ActionType action, object arg, string text, GUIStyle style, params GUILayoutOption[] options) + { + return GUILayout.Button(text, style, options) || mTestAction == action && (mTestActionArg==null || mTestActionArg.Equals(arg)) && UnityEngine.Event.current.type == EventType.Repaint; + } + + + static bool TestButton(eTest_ActionType action, GUIContent text, GUIStyle style, params GUILayoutOption[] options) + { + return GUILayout.Button(text, style, options) || mTestAction == action && UnityEngine.Event.current.type == EventType.Repaint; + } + + static bool TestButtonArg(eTest_ActionType action, object arg, GUIContent text, GUIStyle style, params GUILayoutOption[] options) + { + return GUILayout.Button(text, style, options) || mTestAction == action && (mTestActionArg == null || mTestActionArg.Equals(arg)) && UnityEngine.Event.current.type == EventType.Repaint; + } + + #endregion + + #region Error Management + + static void OnGUI_ShowMsg() + { + if (!string.IsNullOrEmpty(mLocalizationMsg)) + { + GUILayout.BeginHorizontal(); + EditorGUILayout.HelpBox(mLocalizationMsg, mLocalizationMessageType); + + GUILayout.Space(-5); + GUILayout.BeginVertical(GUILayout.Width(15), GUILayout.ExpandHeight(false)); + GUILayout.Space(15); + if (GUILayout.Button("X", GUITools.Style_ToolbarSearchCancelButton, GUILayout.ExpandWidth(false))) + ClearErrors(); + GUILayout.EndVertical(); + GUILayout.EndHorizontal(); + GUILayout.Space(8); + } + } + + static void ShowError ( string Error, bool ShowInConsole = true ) { ShowMessage ( Error, MessageType.Error, ShowInConsole ); } + static void ShowInfo ( string Msg, bool ShowInConsole = false ) { ShowMessage ( Msg, MessageType.Info, ShowInConsole ); } + static void ShowWarning( string Msg, bool ShowInConsole = true) { ShowMessage ( Msg, MessageType.Warning, ShowInConsole ); } + + static void ShowMessage( string Msg, MessageType msgType, bool ShowInConsole ) + { + if (string.IsNullOrEmpty(Msg)) + Msg = string.Empty; + + mLocalizationMsg = Msg; + mLocalizationMessageType = msgType; + if (ShowInConsole) + { + switch (msgType) + { + case MessageType.Error : Debug.LogError(Msg); break; + case MessageType.Warning : Debug.LogWarning(Msg); break; + default : Debug.Log(Msg); break; + } + } + } + + + public static void ClearErrors() + { + GUI.FocusControl(null); + + mLocalizationMsg = string.Empty; + } + + #endregion + + #region Unity Version branching + + public static string Editor_GetCurrentScene() + { + #if UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 + return EditorApplication.currentScene; + #else + return SceneManager.GetActiveScene().path; + #endif + } + + public static void Editor_MarkSceneDirty() + { + #if UNITY_5_3 || UNITY_5_3_OR_NEWER + EditorSceneManager.MarkSceneDirty(SceneManager.GetActiveScene()); + #else + EditorApplication.MarkSceneDirty(); + #endif + } + + public static void Editor_SaveScene(bool force=false) + { + if (force) + Editor_MarkSceneDirty(); + + #if UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 + EditorApplication.SaveScene (); + #else + EditorSceneManager.SaveOpenScenes(); + #endif + } + + public static void Editor_OpenScene( string sceneName ) + { +#if UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 + if (string.IsNullOrEmpty(sceneName)) + EditorApplication.NewEmptyScene(); + else + EditorApplication.OpenScene(sceneName); +#else + if (string.IsNullOrEmpty(sceneName)) + EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single); + else + EditorSceneManager.OpenScene(sceneName); + #endif + } + + #endregion + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor.cs.meta new file mode 100644 index 00000000..71ed09b3 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: ffd53aaaf6936407d8b087583b0626e9 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Languages.cs b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Languages.cs new file mode 100644 index 00000000..d43cdd78 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Languages.cs @@ -0,0 +1,477 @@ +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + public partial class LocalizationEditor + { + #region Variables + private List mTranslationTerms = new List(); + private Dictionary mTranslationRequests = new Dictionary (); + private bool mAppNameTerm_Expanded; + + private List mLanguageCodePopupList; + + #endregion + + void OnGUI_Languages() + { + //GUILayout.Space(5); + + OnGUI_ShowMsg(); + + OnGUI_LanguageList(); + + OnGUI_StoreIntegration(); + + GUILayout.BeginHorizontal(); + GUILayout.Label(new GUIContent("On Missing Translation:", "What should happen IN-GAME when a term is not yet translated to the current language?"), EditorStyles.boldLabel, GUILayout.Width(200)); + GUILayout.BeginVertical(); + GUILayout.Space(7); + EditorGUILayout.PropertyField(mProp_OnMissingTranslation, GUITools.EmptyContent, GUILayout.Width(165)); + GUILayout.EndVertical(); + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + GUILayout.Label(new GUIContent("Unload Languages At Runtime:", "When playing the game, the plugin will unload all unused languages and only load them when needed"), EditorStyles.boldLabel, GUILayout.Width(200)); + GUILayout.BeginVertical(); + GUILayout.Space(7); + EditorGUILayout.PropertyField(mProp_AllowUnloadingLanguages, GUITools.EmptyContent, GUILayout.Width(165)); + GUILayout.EndVertical(); + GUILayout.EndHorizontal(); + + + + + string firstLanguage = ""; + if (mLanguageSource.mLanguages.Count > 0) + firstLanguage = " (" + mLanguageSource.mLanguages [0].Name + ")"; + + GUILayout.BeginHorizontal(); + GUILayout.Label(new GUIContent("Default Language:", "When the game starts this is the language that will be used until the player manually selects a language"), EditorStyles.boldLabel, GUILayout.Width(160)); + GUILayout.BeginVertical(); + GUILayout.Space(7); + + mProp_IgnoreDeviceLanguage.boolValue = EditorGUILayout.Popup(mProp_IgnoreDeviceLanguage.boolValue?1:0, new[]{"Device Language", "First in List"+firstLanguage}, GUILayout.ExpandWidth(true))==1; + GUILayout.EndVertical(); + GUILayout.EndHorizontal(); + } + + #region GUI Languages + + void OnGUI_LanguageList() + { + GUILayout.BeginHorizontal(EditorStyles.toolbar); + GUILayout.FlexibleSpace(); + GUILayout.Label ("Languages:", EditorStyles.miniLabel, GUILayout.ExpandWidth(false)); + GUILayout.FlexibleSpace(); + GUILayout.Label ("Code:", EditorStyles.miniLabel); + GUILayout.Space(170); + GUILayout.EndHorizontal(); + + //--[ Language List ]-------------------------- + + int IndexLanguageToDelete = -1; + int LanguageToMoveUp = -1; + int LanguageToMoveDown = -1; + GUI.backgroundColor = Color.Lerp(GUITools.LightGray, Color.white, 0.5f); + mScrollPos_Languages = GUILayout.BeginScrollView( mScrollPos_Languages, LocalizeInspector.GUIStyle_OldTextArea, GUILayout.MinHeight (200), /*GUILayout.MaxHeight(Screen.height),*/ GUILayout.ExpandHeight(false)); + GUI.backgroundColor = Color.white; + + if (mLanguageCodePopupList == null || mLanguageCodePopupList.Count==0) + { + mLanguageCodePopupList = GoogleLanguages.GetLanguagesForDropdown("", ""); + mLanguageCodePopupList.Sort(); + mLanguageCodePopupList.Insert(0, string.Empty); + } + + for (int i=0, imax=mProp_Languages.arraySize; i c.Contains(currentCode))); + EditorGUI.BeginChangeCheck(); + Index = EditorGUILayout.Popup(Index, mLanguageCodePopupList.ToArray(), EditorStyles.toolbarPopup, GUILayout.Width(60)); + if (EditorGUI.EndChangeCheck() && Index >= 0) + { + currentCode = mLanguageCodePopupList[Index]; + int i0 = currentCode.IndexOf("["); + int i1 = currentCode.IndexOf("]"); + if (i0 >= 0 && i1 > i0) + Prop_LangCode.stringValue = currentCode.Substring(i0 + 1, i1 - i0 - 1); + else + Prop_LangCode.stringValue = string.Empty; + } + var rect = GUILayoutUtility.GetLastRect(); + GUI.Label(rect, Prop_LangCode.stringValue, EditorStyles.toolbarPopup); + } + else + { + GUILayout.Label(Prop_LangCode.stringValue, EditorStyles.toolbarPopup, GUILayout.Width(60)); + } + + GUILayout.EndHorizontal(); + + GUI.enabled = i0; + if (GUILayout.Button( "\u25B2", EditorStyles.toolbarButton, GUILayout.Width(18))) LanguageToMoveUp = i; + + GUI.enabled = true; + if (GUILayout.Button( new GUIContent("Show", "Preview all localizations into this language"), EditorStyles.toolbarButton, GUILayout.Width(35))) + { + LocalizationManager.SetLanguageAndCode( LanName, Prop_LangCode.stringValue, false, true); + } + + if (TestButtonArg( eTest_ActionType.Button_Languages_TranslateAll, i, new GUIContent("Translate", "Translate all empty terms"), EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) + { + GUITools.DelayedCall( () => TranslateAllToLanguage(LanName)); + } + GUI.enabled = true; + GUI.color = Color.white; + + EditorGUI.BeginChangeCheck(); + isLanguageEnabled = EditorGUILayout.Toggle(isLanguageEnabled, GUILayout.Width(15)); + + var r = GUILayoutUtility.GetLastRect(); + GUI.Label(r, new GUIContent("", "Enable/Disable the language.\nDisabled languages can be used to store data values or to avoid showing Languages that are stil under development")); + + if (EditorGUI.EndChangeCheck()) + { + Prop_Flags.intValue = (Prop_Flags.intValue & ~(int)eLanguageDataFlags.DISABLED) | (isLanguageEnabled ? 0 : (int)eLanguageDataFlags.DISABLED); + } + + GUILayout.EndHorizontal(); + } + + GUILayout.EndScrollView(); + + OnGUI_AddLanguage( mProp_Languages ); + + if (mConnection_WWW!=null || mConnection_Text.Contains("Translating")) + { + // Connection Status Bar + int time = (int)(Time.realtimeSinceStartup % 2 * 2.5); + string Loading = mConnection_Text + ".....".Substring(0, time); + GUI.color = Color.gray; + GUILayout.BeginHorizontal(LocalizeInspector.GUIStyle_OldTextArea); + GUILayout.Label (Loading, EditorStyles.miniLabel); + GUI.color = Color.white; + if (GUILayout.Button("Cancel", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) + { + GoogleTranslation.CancelCurrentGoogleTranslations (); + StopConnectionWWW(); + } + GUILayout.EndHorizontal(); + Repaint(); + } + + if (IndexLanguageToDelete>=0) + { + if (EditorUtility.DisplayDialog ("Confirm delete", "Are you sure you want to delete the selected language", "Yes", "Cancel")) + { + mLanguageSource.RemoveLanguage (mLanguageSource.mLanguages [IndexLanguageToDelete].Name); + serializedObject.Update (); + ParseTerms (true, false, false); + } + } + + if (LanguageToMoveUp>=0) SwapLanguages( LanguageToMoveUp, LanguageToMoveUp-1 ); + if (LanguageToMoveDown>=0) SwapLanguages( LanguageToMoveDown, LanguageToMoveDown+1 ); + } + + void SwapLanguages( int iFirst, int iSecond ) + { + serializedObject.ApplyModifiedProperties(); + LanguageSourceData Source = mLanguageSource; + + SwapValues( Source.mLanguages, iFirst, iSecond ); + foreach (TermData termData in Source.mTerms) + { + SwapValues ( termData.Languages, iFirst, iSecond ); + SwapValues ( termData.Flags, iFirst, iSecond ); + } + serializedObject.Update(); + } + + void SwapValues( List mList, int Index1, int Index2 ) + { + LanguageData temp = mList[Index1]; + mList[Index1] = mList[Index2]; + mList[Index2] = temp; + } + void SwapValues( string[] mList, int Index1, int Index2 ) + { + string temp = mList[Index1]; + mList[Index1] = mList[Index2]; + mList[Index2] = temp; + } + void SwapValues( byte[] mList, int Index1, int Index2 ) + { + byte temp = mList[Index1]; + mList[Index1] = mList[Index2]; + mList[Index2] = temp; + } + + + void OnGUI_AddLanguage( SerializedProperty Prop_Languages) + { + //--[ Add Language Upper Toolbar ]----------------- + + GUILayout.BeginVertical(); + GUILayout.BeginHorizontal(); + + GUILayout.BeginHorizontal(EditorStyles.toolbar); + mLanguages_NewLanguage = EditorGUILayout.TextField("", mLanguages_NewLanguage, EditorStyles.toolbarTextField, GUILayout.ExpandWidth(true)); + GUILayout.EndHorizontal(); + + GUI.enabled = !string.IsNullOrEmpty (mLanguages_NewLanguage); + if (TestButton(eTest_ActionType.Button_AddLanguageManual,"Add", EditorStyles.toolbarButton, GUILayout.Width(50))) + { + Prop_Languages.serializedObject.ApplyModifiedProperties(); + mLanguageSource.AddLanguage( mLanguages_NewLanguage, GoogleLanguages.GetLanguageCode(mLanguages_NewLanguage) ); + Prop_Languages.serializedObject.Update(); + mLanguages_NewLanguage = ""; + GUI.FocusControl(string.Empty); + } + GUI.enabled = true; + + GUILayout.EndHorizontal(); + + + //--[ Add Language Bottom Toolbar ]----------------- + + GUILayout.BeginHorizontal(); + + //-- Language Dropdown ----------------- + string CodesToExclude = string.Empty; + foreach (var LanData in mLanguageSource.mLanguages) + CodesToExclude = string.Concat(CodesToExclude, "[", LanData.Code, "]"); + + List Languages = GoogleLanguages.GetLanguagesForDropdown(mLanguages_NewLanguage, CodesToExclude); + + GUI.changed = false; + int index = EditorGUILayout.Popup(0, Languages.ToArray(), EditorStyles.toolbarDropDown); + + if (GUI.changed && index>=0) + { + mLanguages_NewLanguage = GoogleLanguages.GetFormatedLanguageName( Languages[index] ); + } + + + if (TestButton(eTest_ActionType.Button_AddLanguageFromPopup, "Add", EditorStyles.toolbarButton, GUILayout.Width(50)) && index>=0) + { + Prop_Languages.serializedObject.ApplyModifiedProperties(); + mLanguages_NewLanguage = GoogleLanguages.GetFormatedLanguageName(Languages[index]); + + if (!string.IsNullOrEmpty(mLanguages_NewLanguage)) + mLanguageSource.AddLanguage(mLanguages_NewLanguage, GoogleLanguages.GetLanguageCode(mLanguages_NewLanguage)); + Prop_Languages.serializedObject.Update(); + + mLanguages_NewLanguage = ""; + GUI.FocusControl(string.Empty); + } + + GUILayout.EndHorizontal(); + GUILayout.EndVertical(); + GUI.color = Color.white; + } + + + void TranslateAllToLanguage (string lanName) + { + if (!GoogleTranslation.CanTranslate ()) + { + ShowError ("WebService is not set correctly or needs to be reinstalled"); + return; + } + ClearErrors(); + int LanIndex = mLanguageSource.GetLanguageIndex (lanName); + string code = mLanguageSource.mLanguages [LanIndex].Code; + string googleCode = GoogleLanguages.GetGoogleLanguageCode(code); + if (string.IsNullOrEmpty(googleCode)) + { + ShowError("Language '" + code + "' is not supported by google translate"); + return; + } + googleCode = code; + + mTranslationTerms.Clear (); + mTranslationRequests.Clear (); + foreach (var termData in mLanguageSource.mTerms) + { + if (termData.TermType != eTermType.Text) + continue; + + if (!string.IsNullOrEmpty(termData.Languages[LanIndex])) + continue; + + string sourceCode, sourceText; + FindTranslationSource( LanguageSourceData.GetKeyFromFullTerm(termData.Term), termData, code, null, out sourceText, out sourceCode ); + + mTranslationTerms.Add (termData.Term); + + GoogleTranslation.CreateQueries(sourceText, sourceCode, googleCode, mTranslationRequests); // can split plurals into several queries + } + + if (mTranslationRequests.Count == 0) + { + StopConnectionWWW (); + return; + } + + mConnection_WWW = null; + mConnection_Text = "Translating"; if (mTranslationRequests.Count > 1) mConnection_Text += " (" + mTranslationRequests.Count + ")"; + mConnection_Callback = null; + //EditorApplication.update += CheckForConnection; + + GoogleTranslation.Translate (mTranslationRequests, OnLanguageTranslated); + } + + void OnLanguageTranslated( Dictionary requests, string Error ) + { + //Debug.Log (Result); + + //if (Result.Contains("Service invoked too many times")) + //{ + // TimeStartTranslation = EditorApplication.timeSinceStartup + 1; + // EditorApplication.update += DelayedStartTranslation; + // mConnection_Text = "Translating (" + mTranslationRequests.Count + ")"; + // return; + //} + + //if (!string.IsNullOrEmpty(Error))/* || !Result.Contains("")*/ + //{ + // Debug.LogError("WEB ERROR: " + Error); + // ShowError ("Unable to access Google or not valid request"); + // return; + //} + + ClearErrors(); + StopConnectionWWW(); + + if (!string.IsNullOrEmpty(Error)) + { + ShowError (Error); + return; + } + + if (requests.Values.Count == 0) + return; + + var langCode = requests.Values.First().TargetLanguagesCode [0]; + //langCode = GoogleLanguages.GetGoogleLanguageCode(langCode); + int langIndex = mLanguageSource.GetLanguageIndexFromCode (langCode, false); + //if (langIndex >= 0) + { + foreach (var term in mTranslationTerms) + { + var termData = mLanguageSource.GetTermData(term); + if (termData == null) + continue; + if (termData.TermType != eTermType.Text) + continue; + //if (termData.Languages.Length <= langIndex) + // continue; + + string sourceCode, sourceText; + FindTranslationSource(LanguageSourceData.GetKeyFromFullTerm(termData.Term), termData, langCode, null, out sourceText, out sourceCode); + + string result = GoogleTranslation.RebuildTranslation(sourceText, mTranslationRequests, langCode); // gets the result from google and rebuilds the text from multiple queries if its is plurals + + termData.Languages[langIndex] = result; + } + } + + mTranslationTerms.Clear (); + mTranslationRequests.Clear (); + StopConnectionWWW (); + } + + #endregion + + #region Store Integration + + void OnGUI_StoreIntegration() + { + GUIStyle lstyle = new GUIStyle (EditorStyles.label); + lstyle.richText = true; + + GUILayout.BeginHorizontal (); + GUILayout.Label (new GUIContent("Store Integration:", "Setups the stores to detect that the game has localization, Android adds strings.xml for each language. IOS modifies the Info.plist"), EditorStyles.boldLabel, GUILayout.Width(160)); + GUILayout.FlexibleSpace(); + + GUILayout.Label( new GUIContent( "\u2713 IOS", "Setups the stores to show in iTunes and the Appstore all the languages that this app supports, also localizes the app name if available" ), lstyle, GUILayout.Width( 90 ) ); + GUILayout.Label( new GUIContent( "\u2713 Android", "Setups the stores to show in GooglePlay all the languages this app supports, also localizes the app name if available" ), lstyle, GUILayout.Width( 90 ) ); + GUILayout.EndHorizontal (); + + GUILayout.BeginHorizontal(); + mAppNameTerm_Expanded = GUILayout.Toggle(mAppNameTerm_Expanded, new GUIContent( "App Name translations:", "How should the game be named in the devices based on their language" ), EditorStyles.foldout, GUILayout.Width( 160 ) ); + + GUILayout.Label("", GUILayout.ExpandWidth(true)); + var rect = GUILayoutUtility.GetLastRect(); + TermsPopup_Drawer.ShowGUI( rect, mProp_AppNameTerm, GUITools.EmptyContent, mLanguageSource); + + if (GUILayout.Button("New Term", EditorStyles.miniButton, GUILayout.ExpandWidth(false))) + { + AddLocalTerm("App_Name"); + mProp_AppNameTerm.stringValue = "App_Name"; + mAppNameTerm_Expanded = true; + } + GUILayout.EndHorizontal(); + + if (mAppNameTerm_Expanded) + { + GUILayout.BeginHorizontal(); + GUILayout.Space(10); + + GUILayout.BeginVertical("Box"); + var termName = mProp_AppNameTerm.stringValue; + if (!string.IsNullOrEmpty(termName)) + { + var termData = LocalizationManager.GetTermData(termName); + if (termData != null) + OnGUI_Keys_Languages(mProp_AppNameTerm.stringValue, ref termData, null, true, mLanguageSource); + } + GUILayout.Space(10); + + GUILayout.BeginHorizontal(); + GUILayout.Label("Default App Name:", lstyle, GUITools.DontExpandWidth); + GUILayout.Label(Application.productName); + GUILayout.EndHorizontal(); + GUILayout.EndVertical(); + + GUILayout.EndHorizontal(); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Languages.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Languages.cs.meta new file mode 100644 index 00000000..375ebe73 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Languages.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 304783c1e95d94a598aecd17728c8556 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Spreadsheet_Google.cs b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Spreadsheet_Google.cs new file mode 100644 index 00000000..221d3b42 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Spreadsheet_Google.cs @@ -0,0 +1,727 @@ +using System; +using System.Collections.Generic; +using System.Text; +using UnityEditor; +using UnityEngine; +using UnityEngine.Networking; +using TEngine.Localization.SimpleJSON; + +namespace TEngine.Localization +{ + public partial class LocalizationEditor + { + #region Variables + + public static Dictionary mGoogleSpreadsheets = new Dictionary(StringComparer.Ordinal); + + public UnityWebRequest mConnection_WWW; + + delegate void fnConnectionCallback(string Result, string Error); + event fnConnectionCallback mConnection_Callback; + //float mConnection_TimeOut; + + string mConnection_Text = string.Empty; + + string mWebService_Status; + + #endregion + + #region GUI + + void OnGUI_Spreadsheet_Google() + { + GUILayout.Space(20); + +#if UNITY_WEBPLAYER + mConnection_Text = string.Empty; + EditorGUILayout.HelpBox("Google Synchronization is not supported when in WebPlayer mode." + mConnection_Text, MessageType.Info); + + mProp_GoogleUpdateFrequency.enumValueIndex = mProp_GoogleUpdateFrequency.enumValueIndex; // to avoid the warning "unused" + mProp_GoogleUpdateSynchronization.enumValueIndex = mProp_GoogleUpdateSynchronization.enumValueIndex; +#else + + OnGUI_GoogleCredentials(); + + OnGUI_ShowMsg(); + + if (string.IsNullOrEmpty(mProp_Google_WebServiceURL.stringValue)) + return; + + if (mWebService_Status == "Offline") + return; + + GUILayout.Space(20); + + GUI.backgroundColor = Color.Lerp(Color.gray, Color.white, 0.5f); + GUILayout.BeginVertical(LocalizeInspector.GUIStyle_OldTextArea, GUILayout.Height (1)); + GUI.backgroundColor = Color.white; + GUILayout.Space(10); + + GUILayout.BeginHorizontal(); + GUILayout.Label(new GUIContent(" Password", "This should match the value of the LocalizationPassword variable in the WebService Script in your Google Drive"), GUILayout.Width(108)); + mProp_Google_Password.stringValue = EditorGUILayout.TextField(mProp_Google_Password.stringValue, GUILayout.ExpandWidth(true)); + GUILayout.EndHorizontal(); + + OnGUI_GoogleSpreadsheetsInGDrive(); + GUILayout.EndVertical(); + + if (mConnection_WWW!=null) + { + // Connection Status Bar + int time = (int)(Time.realtimeSinceStartup % 2 * 2.5); + string Loading = mConnection_Text + ".....".Substring(0, time); + GUI.color = Color.gray; + GUILayout.BeginHorizontal(LocalizeInspector.GUIStyle_OldTextArea); + GUILayout.Label (Loading, EditorStyles.miniLabel); + GUI.color = Color.white; + if (GUILayout.Button("Cancel", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) + StopConnectionWWW(); + GUILayout.EndHorizontal(); + Repaint(); + } + //else + // GUILayout.Space(10); + + + EditorGUI.BeginChangeCheck(); + GUILayout.Space(5); + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + LanguageSourceData.eGoogleUpdateFrequency GoogleUpdateFrequency = (LanguageSourceData.eGoogleUpdateFrequency)mProp_GoogleUpdateFrequency.enumValueIndex; + GoogleUpdateFrequency = (LanguageSourceData.eGoogleUpdateFrequency)EditorGUILayout.EnumPopup("Auto Update Frequency", GoogleUpdateFrequency, GUILayout.ExpandWidth(true)); + if (EditorGUI.EndChangeCheck()) + { + mProp_GoogleUpdateFrequency.enumValueIndex = (int)GoogleUpdateFrequency; + } + + GUILayout.Space(10); + GUILayout.Label("Delay:"); + mProp_GoogleUpdateDelay.floatValue = EditorGUILayout.FloatField(mProp_GoogleUpdateDelay.floatValue, GUILayout.Width(30)); + GUILayout.Label("secs"); + + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + var GoogleInEditorCheckFrequency = (LanguageSourceData.eGoogleUpdateFrequency)mProp_GoogleInEditorCheckFrequency.enumValueIndex; + EditorGUI.BeginChangeCheck(); + GoogleInEditorCheckFrequency = (LanguageSourceData.eGoogleUpdateFrequency)EditorGUILayout.EnumPopup(new GUIContent("In-Editor Check Frequency", "How often the editor will verify that the Spreadsheet is up-to-date with the LanguageSource. Having un-synchronized Spreadsheets can lead to issues when playing in the device as the download data will override the one in the build"), GoogleInEditorCheckFrequency, GUILayout.ExpandWidth(false)); + if (EditorGUI.EndChangeCheck()) + { + mProp_GoogleInEditorCheckFrequency.enumValueIndex = (int)GoogleInEditorCheckFrequency; + } + GUILayout.Space(122); + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUILayout.Label("Update Synchronization", GUILayout.Width(180)); + EditorGUI.BeginChangeCheck(); + LanguageSourceData.eGoogleUpdateSynchronization GoogleUpdateSynchronization = (LanguageSourceData.eGoogleUpdateSynchronization)mProp_GoogleUpdateSynchronization.enumValueIndex; + GoogleUpdateSynchronization = (LanguageSourceData.eGoogleUpdateSynchronization)EditorGUILayout.EnumPopup(GoogleUpdateSynchronization, GUILayout.Width(178)); + if (EditorGUI.EndChangeCheck()) + { + mProp_GoogleUpdateSynchronization.enumValueIndex = (int)GoogleUpdateSynchronization; + } + GUILayout.EndHorizontal(); + + GUILayout.Space(5); + + GUI.changed = false; + bool OpenDataSourceAfterExport = EditorPrefs.GetBool("I2Loc OpenDataSourceAfterExport", true); + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + OpenDataSourceAfterExport = GUILayout.Toggle(OpenDataSourceAfterExport, "Open Spreadsheet after Export"); + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + if (GUI.changed) + { + GUI.changed = false; + EditorPrefs.SetBool("I2Loc OpenDataSourceAfterExport", OpenDataSourceAfterExport); + } + +#endif + + GUILayout.Space(5); + } + + void OnGUI_GoogleCredentials() + { + GUI.enabled = mConnection_WWW==null; + + GUI.changed = false; + + string WebServiceHelp = "The web service is a script running on the google drive where the spreadsheet you want to use is located.\nThat script allows the game to synchronize the localization even after the game is published."; + + GUILayout.BeginHorizontal(); + GUILayout.Label (new GUIContent("Web Service URL:", WebServiceHelp), GUILayout.Width(110)); + + GUI.SetNextControlName ("WebServiceURL"); + mProp_Google_WebServiceURL.stringValue = EditorGUILayout.TextField(mProp_Google_WebServiceURL.stringValue); + + if (!string.IsNullOrEmpty(mWebService_Status)) + { + if (mWebService_Status=="Online") + { + GUI.color = Color.green; + GUILayout.Label( "", GUILayout.Width(17)); + Rect r = GUILayoutUtility.GetLastRect(); r.xMin += 3; r.yMin-= 3; r.xMax+= 2; r.yMax+=2; + GUI.Label( r, new GUIContent("\u2713", "Online"), EditorStyles.whiteLargeLabel); + GUI.color = Color.white; + } + else + if (mWebService_Status=="Offline") + { + GUI.color = Color.red; + GUILayout.Label( "", GUILayout.Width(17)); + Rect r = GUILayoutUtility.GetLastRect(); r.xMin += 3; r.yMin-= 3; r.xMax+= 2; r.yMax+=2; + GUI.Label( r, new GUIContent("\u2717", mWebService_Status), EditorStyles.whiteLargeLabel); + GUI.color = Color.white; + } + else + if (mWebService_Status=="UnsupportedVersion") + { + Rect rect = GUILayoutUtility.GetLastRect(); + float Width = 15; + rect.xMin = rect.xMax+1; + rect.xMax = rect.xMin + rect.height; + GUITools.DrawSkinIcon(rect, "CN EntryWarnIcon", "CN EntryWarn"); + GUI.Label(rect, new GUIContent("\u2717", "The current Google WebService is not supported.\nPlease, delete the WebService from the Google Drive and Install the latest version.")); + GUILayout.Space (Width); + } + } + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + GUILayout.Space (118); + if (GUILayout.Button(new GUIContent("Install", "This opens the Web Service Script and shows you steps to install and authorize it on your Google Drive"), EditorStyles.toolbarButton)) + { + ClearErrors(); + Application.OpenURL("https://script.google.com/d/1zcsLSmq3Oddd8AsLuoKNDG1Y0eYBOHzyvGT7v94u1oN6igmsZb_PJzEm/newcopy"); // V5 + //Application.OpenURL("https://goo.gl/RBCO0o"); // V4:https://script.google.com/d/1T7e5_40NcgRyind-yeg4PAkHz9TNZJ22F4RcbOvCpAs03JNf1vKNNTZB/newcopy + //Application.OpenURL("https://goo.gl/wFSbv2");// V3:https://script.google.com/d/1CxQDSXflsXRaH3M7xGfrIDrFwOIHWPsYTWi4mRZ_k77nyIInTgIk63Kd/newcopy"); + } + if (GUILayout.Button("Verify", EditorStyles.toolbarButton)) + { + ClearErrors(); + VerifyGoogleService(mProp_Google_WebServiceURL.stringValue); + GUI.changed = false; + } + GUILayout.EndHorizontal(); + + + if (string.IsNullOrEmpty(mProp_Google_WebServiceURL.stringValue)) + { + EditorGUILayout.HelpBox(WebServiceHelp, MessageType.Info); + } + + if (GUI.changed) + { + if (string.IsNullOrEmpty(mProp_Google_WebServiceURL.stringValue)) + { + mProp_Google_SpreadsheetKey.stringValue = string.Empty; + mProp_Google_SpreadsheetName.stringValue = string.Empty; + } + + + // If the web service changed then clear the cached spreadsheet keys + mGoogleSpreadsheets.Clear(); + + GUI.changed = false; + ClearErrors(); + } + GUI.enabled = true; + } + + void OnGUI_GoogleSpreadsheetsInGDrive() + { + GUI.enabled = mConnection_WWW==null; + + string[] Spreadsheets; + string[] SpreadsheetsKey; + if (mGoogleSpreadsheets.Count>0 || string.IsNullOrEmpty(mProp_Google_SpreadsheetKey.stringValue)) + { + Spreadsheets = new List(mGoogleSpreadsheets.Keys).ToArray(); + SpreadsheetsKey = new List(mGoogleSpreadsheets.Values).ToArray(); + } + else + { + Spreadsheets = new[]{mProp_Google_SpreadsheetName.stringValue ?? string.Empty}; + SpreadsheetsKey = new[]{mProp_Google_SpreadsheetKey.stringValue ?? string.Empty}; + } + int mSpreadsheetIndex = Array.IndexOf(SpreadsheetsKey, mProp_Google_SpreadsheetKey.stringValue); + + //--[ Spreadsheets ]------------------ + GUILayout.BeginHorizontal(); + GUILayout.Space(10); + GUILayout.Label ("In Google Drive:", GUILayout.Width(100)); + + GUI.changed = false; + GUI.enabled = Spreadsheets != null && Spreadsheets.Length>0; + mSpreadsheetIndex = EditorGUILayout.Popup(mSpreadsheetIndex, Spreadsheets, EditorStyles.toolbarPopup); + if (GUI.changed && mSpreadsheetIndex >= 0) + { + mProp_Google_SpreadsheetKey.stringValue = SpreadsheetsKey[mSpreadsheetIndex]; + mProp_Google_SpreadsheetName.stringValue = Spreadsheets[mSpreadsheetIndex]; + GUI.changed = false; + } + GUI.enabled = true; + + GUI.enabled = !string.IsNullOrEmpty(mProp_Google_SpreadsheetKey.stringValue) && mConnection_WWW==null; + if (GUILayout.Button("X", EditorStyles.toolbarButton,GUILayout.ExpandWidth(false))) + mProp_Google_SpreadsheetKey.stringValue = string.Empty; + GUI.enabled = true; + GUILayout.Space(10); + GUILayout.EndHorizontal(); + + GUILayout.Space(2); + + //--[ Spreadsheets Operations ]------------------ + GUILayout.BeginHorizontal(); + GUILayout.Space(114); + if (GUILayout.Button("New", EditorStyles.toolbarButton,GUILayout.ExpandWidth(true))) + Google_NewSpreadsheet(); + + GUI.enabled = !string.IsNullOrEmpty(mProp_Google_SpreadsheetKey.stringValue) && mConnection_WWW==null; + if (GUILayout.Button("Open", EditorStyles.toolbarButton,GUILayout.ExpandWidth(true))) + OpenGoogleSpreadsheet(mProp_Google_SpreadsheetKey.stringValue); + GUI.enabled = mConnection_WWW==null; + + GUILayout.Space(5); + + if (TestButton(eTest_ActionType.Button_GoogleSpreadsheet_RefreshList, "Refresh", EditorStyles.toolbarButton,GUILayout.ExpandWidth(true))) + EditorApplication.update+=Google_FindSpreadsheets; + + GUILayout.Space(10); + GUILayout.EndHorizontal(); + + GUILayout.Space(15); + + if (!string.IsNullOrEmpty(mProp_Google_SpreadsheetKey.stringValue)) + OnGUI_GoogleButtons_ImportExport( mProp_Google_SpreadsheetKey.stringValue ); + + GUI.enabled = true; + } + + + private void OnGUI_ImportButtons() + { + eSpreadsheetUpdateMode Mode = SynchronizationButtons("Import"); + if (Mode != eSpreadsheetUpdateMode.None || InTestAction(eTest_ActionType.Button_GoogleSpreadsheet_Import)) + { + if (mTestAction == eTest_ActionType.Button_GoogleSpreadsheet_Import) + Mode = (eSpreadsheetUpdateMode)mTestActionArg; + + serializedObject.ApplyModifiedProperties(); + + var modeCopy = Mode; + GUITools.DelayedCall(() => Import_Google(modeCopy)); + } + } + + private void OnGUI_ExportButtons() + { + eSpreadsheetUpdateMode Mode = SynchronizationButtons("Export"); + if (Mode != eSpreadsheetUpdateMode.None || InTestAction(eTest_ActionType.Button_GoogleSpreadsheet_Export)) + { + if (mTestAction == eTest_ActionType.Button_GoogleSpreadsheet_Export) + Mode = (eSpreadsheetUpdateMode)mTestActionArg; + + serializedObject.ApplyModifiedProperties(); + + var modeCopy = Mode; + GUITools.DelayedCall(() => Export_Google(modeCopy)); + } + } + + void OnGUI_GoogleButtons_ImportExport( string SpreadsheetKey ) + { + GUI.enabled = !string.IsNullOrEmpty(SpreadsheetKey) && mConnection_WWW==null; + + bool vertical = EditorGUIUtility.currentViewWidth < 450; + + if (vertical) + { + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + OnGUI_ImportButtons(); + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + OnGUI_ExportButtons(); + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + } + else + { + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + OnGUI_ImportButtons(); + GUILayout.FlexibleSpace(); + OnGUI_ExportButtons(); + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + } + + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUILayout.BeginVertical(); + EditorGUIUtility.labelWidth += 10; + EditorGUILayout.PropertyField(mProp_Spreadsheet_SpecializationAsRows, new GUIContent("Show Specializations as Rows", "true: Make each specialization a separate row (e.g. Term[VR]..., Term[Touch]....\nfalse: Merge specializations into same cell separated by [i2s_XXX]")); + EditorGUILayout.PropertyField(mProp_Spreadsheet_SortRows, new GUIContent("Sort Rows", "true: Sort each term by its name....\nfalse: Keep the terms order")); + EditorGUIUtility.labelWidth -= 10; + GUILayout.EndVertical(); + GUILayout.EndHorizontal(); + GUILayout.Space(10); + + + GUI.enabled = true; + } + + eSpreadsheetUpdateMode SynchronizationButtons( string Operation, bool ForceReplace = false ) + { + eSpreadsheetUpdateMode Result = eSpreadsheetUpdateMode.None; + GUILayout.BeginVertical(LocalizeInspector.GUIStyle_OldTextArea, GUILayout.Width (1)); + GUI.backgroundColor = Color.white; + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + GUILayout.Label(Operation, EditorStyles.miniLabel); + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + if (GUILayout.Button( "Replace", EditorStyles.toolbarButton, GUILayout.Width(60))) + Result = eSpreadsheetUpdateMode.Replace; + + if (ForceReplace) GUI.enabled = false; + if (GUILayout.Button( "Merge", EditorStyles.toolbarButton, GUILayout.Width(60))) + Result = eSpreadsheetUpdateMode.Merge; + + if (GUILayout.Button( "Add New", EditorStyles.toolbarButton, GUILayout.Width(60))) + Result = eSpreadsheetUpdateMode.AddNewTerms; + GUI.enabled = mConnection_WWW==null; + GUILayout.Space(1); + GUILayout.EndHorizontal(); + + GUILayout.Space(2); + GUILayout.EndVertical(); + + if (Result != eSpreadsheetUpdateMode.None) + ClearErrors(); + + return Result; + } + #endregion + + void VerifyGoogleService( string WebServiceURL ) + { + #if UNITY_WEBPLAYER + ShowError ("Contacting google translation is not yet supported on WebPlayer" ); + #else + StopConnectionWWW(); + mWebService_Status = null; + mConnection_WWW = UnityWebRequest.Get(WebServiceURL + "?action=Ping"); + I2Utils.SendWebRequest(mConnection_WWW); + mConnection_Callback = OnVerifyGoogleService; + EditorApplication.update += CheckForConnection; + mConnection_Text = "Verifying Web Service"; + //mConnection_TimeOut = Time.realtimeSinceStartup + 10; + #endif + } + + void OnVerifyGoogleService( string Result, string Error ) + { + if (Result.Contains("Authorization is required to perform that action")) + { + ShowWarning("You need to authorize the webservice before using it. Check the steps 4 and 5 in the WebService Script"); + mWebService_Status = "Offline"; + return; + } + + try + { + var data = JSON.Parse(Result).AsObject; + int version = 0; + if (!int.TryParse(data["script_version"], out version)) + version = 0; + int requiredVersion = LocalizationManager.GetRequiredWebServiceVersion(); + + if (requiredVersion == version) + { + mWebService_Status = "Online"; + ClearErrors(); + } + else + { + mWebService_Status = "UnsupportedVersion"; + ShowError("The current Google WebService is not supported.\nPlease, delete the WebService from the Google Drive and Install the latest version."); + } + } + catch (Exception) + { + ShowError("Unable to access the WebService"); + mWebService_Status = "Offline"; + } + } + + + void Export_Google( eSpreadsheetUpdateMode UpdateMode ) + { + StopConnectionWWW(); + LanguageSourceData source = GetSourceData(); + mConnection_WWW = source.Export_Google_CreateWWWcall( UpdateMode ); + if (mConnection_WWW==null) + { + OnExported_Google(string.Empty, "WebPlayer can't contact Google"); + } + else + { + mConnection_Callback = OnExported_Google; + EditorApplication.update += CheckForConnection; + mConnection_Text = "Uploading spreadsheet"; + //mConnection_TimeOut = Time.realtimeSinceStartup + 10; + } + } + + void OnExported_Google( string Result, string Error ) + { + // Checkf or error, but discard the "necessary data rewind wasn't possible" as thats not a real error, just a bug in Unity with POST redirects + if (!string.IsNullOrEmpty(Error) && !Error.Contains("rewind")) + { + Debug.Log (Error); + ShowError("Unable to access google"); + return; + } + + if (EditorPrefs.GetBool("I2Loc OpenDataSourceAfterExport", true) && !string.IsNullOrEmpty(GetSourceData().Google_SpreadsheetName)) + OpenGoogleSpreadsheet(GetSourceData().Google_SpreadsheetKey ); + mProp_GoogleLiveSyncIsUptoDate.boolValue = true; + } + + static void OpenGoogleSpreadsheet( string SpreadsheetKey ) + { + ClearErrors(); + string SpreadsheetUrl = "https://docs.google.com/spreadsheet/ccc?key=" + SpreadsheetKey; + Application.OpenURL(SpreadsheetUrl); + } + + public abstract LanguageSourceData GetSourceData(); + + + void Import_Google( eSpreadsheetUpdateMode UpdateMode ) + { + StopConnectionWWW(); + LanguageSourceData source = GetSourceData(); + mConnection_WWW = source.Import_Google_CreateWWWcall(true, false); + if (mConnection_WWW==null) + { + OnImported_Google(string.Empty, "Unable to import from google", eSpreadsheetUpdateMode.Replace); + } + else + { + mConnection_Callback=null; + switch (UpdateMode) + { + case eSpreadsheetUpdateMode.Replace : mConnection_Callback += OnImported_Google_Replace; break; + case eSpreadsheetUpdateMode.Merge : mConnection_Callback += OnImported_Google_Merge; break; + case eSpreadsheetUpdateMode.AddNewTerms : mConnection_Callback += OnImported_Google_AddNewTerms; break; + } + EditorApplication.update += CheckForConnection; + mConnection_Text = "Downloading spreadsheet"; + //mConnection_TimeOut = Time.realtimeSinceStartup + 10; + } + } + + void OnImported_Google_Replace( string Result, string Error ) { OnImported_Google(Result, Error, eSpreadsheetUpdateMode.Replace); } + void OnImported_Google_Merge( string Result, string Error ) { OnImported_Google(Result, Error, eSpreadsheetUpdateMode.Merge); } + void OnImported_Google_AddNewTerms( string Result, string Error ) { OnImported_Google(Result, Error, eSpreadsheetUpdateMode.AddNewTerms); } + + void OnImported_Google( string Result, string Error, eSpreadsheetUpdateMode UpdateMode ) + { + if (!string.IsNullOrEmpty(Error)) + { + Debug.Log(Error); + ShowError("Unable to access google"); + return; + } + LanguageSourceData source = GetSourceData(); + string ErrorMsg = source.Import_Google_Result(Result, UpdateMode); + bool HasErrors = !string.IsNullOrEmpty(ErrorMsg); + if (HasErrors) + ShowError(ErrorMsg); + + serializedObject.Update(); + ParseTerms(true, false, !HasErrors); + mSelectedKeys.Clear (); + mSelectedCategories.Clear(); + ScheduleUpdateTermsToShowInList(); + mLanguageSource.GetCategories(false, mSelectedCategories); + + EditorUtility.SetDirty (target); + AssetDatabase.SaveAssets(); + } + + void CheckForConnection() + { + if (mConnection_WWW!=null && mConnection_WWW.isDone) + { + fnConnectionCallback callback = mConnection_Callback; + string Result = string.Empty; + string Error = mConnection_WWW.error; + + if (string.IsNullOrEmpty(Error)) + { + // Try first converting with the right encoding + var bytes = mConnection_WWW.downloadHandler.data; + if (bytes!=null) + Result = Encoding.UTF8.GetString(bytes); //mConnection_WWW.text; + + // Fallback to use the default encoding + else + Result = mConnection_WWW.downloadHandler.text; + } + + StopConnectionWWW(); + if (callback!=null) + callback(Result, Error); + } + /*else + if (Time.realtimeSinceStartup > mConnection_TimeOut+30) + { + fnConnectionCallback callback = mConnection_Callback; + StopConnectionWWW(); + if (callback!=null) + callback(string.Empty, "Time Out"); + }*/ + } + + void StopConnectionWWW() + { + EditorApplication.update -= CheckForConnection; + mConnection_WWW = null; + mConnection_Callback = null; + mConnection_Text = string.Empty; + } + + #region New Spreadsheet + + void Google_NewSpreadsheet() + { + #if UNITY_WEBPLAYER + ShowError ("Contacting google translation is not yet supported on WebPlayer" ); + #else + + ClearErrors(); + string SpreadsheetName; + + LanguageSourceData source = GetSourceData(); + if (source.IsGlobalSource()) + SpreadsheetName = string.Format("{0} Localization", PlayerSettings.productName); + else + SpreadsheetName = string.Format("{0} {1} {2}", PlayerSettings.productName, Editor_GetCurrentScene(), source.ownerObject.name); + + string query = mProp_Google_WebServiceURL.stringValue + "?action=NewSpreadsheet&name=" + Uri.EscapeDataString(SpreadsheetName) + "&password="+ Uri.EscapeDataString(mProp_Google_Password.stringValue); + + mConnection_WWW = UnityWebRequest.Get(query); + I2Utils.SendWebRequest(mConnection_WWW); + mConnection_Callback = Google_OnNewSpreadsheet; + EditorApplication.update += CheckForConnection; + mConnection_Text = "Creating Spreadsheet"; + //mConnection_TimeOut = Time.realtimeSinceStartup + 10; + #endif + } + + void Google_OnNewSpreadsheet( string Result, string Error ) + { + if (!string.IsNullOrEmpty(Error)) + { + ShowError("Unable to access google"); + return; + } + if (Result=="Wrong Password") + { + ShowError(Result); + return; + } + + try + { + var data = JSON.Parse(Result).AsObject; + + string name = data["name"]; + string key = data["id"]; + + serializedObject.Update(); + mProp_Google_SpreadsheetKey.stringValue = key; + mProp_Google_SpreadsheetName.stringValue = name; + serializedObject.ApplyModifiedProperties(); + mGoogleSpreadsheets[name] = key; + + LanguageSourceData source = GetSourceData(); + if (source.mTerms.Count>0 || source.mLanguages.Count>0) + Export_Google( eSpreadsheetUpdateMode.Replace ); + else + if (EditorPrefs.GetBool("I2Loc OpenDataSourceAfterExport", true)) + OpenGoogleSpreadsheet( key ); + + } + catch(Exception e) + { + ShowError (e.Message); + } + } + + #endregion + + #region FindSpreadsheets + + void Google_FindSpreadsheets() + { + ClearErrors(); + EditorApplication.update -= Google_FindSpreadsheets; + string query = mProp_Google_WebServiceURL.stringValue + "?action=GetSpreadsheetList&password="+ Uri.EscapeDataString(mProp_Google_Password.stringValue); + mConnection_WWW = UnityWebRequest.Get(query); + I2Utils.SendWebRequest(mConnection_WWW); + mConnection_Callback = Google_OnFindSpreadsheets; + EditorApplication.update += CheckForConnection; + mConnection_Text = "Accessing google"; + //mConnection_TimeOut = Time.realtimeSinceStartup + 10; + } + + void Google_OnFindSpreadsheets( string Result, string Error) + { + if (!string.IsNullOrEmpty(Error)) + { + ShowError("Unable to access google"); + return; + } + + if (Result=="Wrong Password") + { + ShowError(Result); + return; + } + + try + { + mGoogleSpreadsheets.Clear(); + var data = JSON.Parse(Result).AsObject; + foreach (KeyValuePair element in data) + mGoogleSpreadsheets[element.Key] = element.Value; + } + catch(Exception e) + { + ShowError (e.Message); + } + + } + + #endregion + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Spreadsheet_Google.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Spreadsheet_Google.cs.meta new file mode 100644 index 00000000..77261f67 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Spreadsheet_Google.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 800caf7e364ec2947be099b4f9ed976d +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Spreadsheet_Local.cs b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Spreadsheet_Local.cs new file mode 100644 index 00000000..ade61d17 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Spreadsheet_Local.cs @@ -0,0 +1,339 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + public partial class LocalizationEditor + { + enum eLocalSpreadsheeet { CSV, XLS, XLSX, NONE } + + void OnGUI_Spreadsheet_Local() + { + GUILayout.Space(10); + GUILayout.BeginVertical(); + + GUILayout.BeginHorizontal(); + GUILayout.Label ("File:", GUILayout.ExpandWidth(false)); + + mProp_Spreadsheet_LocalFileName.stringValue = EditorGUILayout.TextField(mProp_Spreadsheet_LocalFileName.stringValue); + /*if (GUILayout.Button("...", "toolbarbutton", GUILayout.ExpandWidth(false))) + { + string sFileName = mProp_Spreadsheet_LocalFileName.stringValue; + + string sPath = string.Empty; + try { + sPath = System.IO.Path.GetDirectoryName(sFileName); + } + catch( System.Exception e){} + + if (string.IsNullOrEmpty(sPath)) + sPath = Application.dataPath + "/"; + + sFileName = System.IO.Path.GetFileName(sFileName); + if (string.IsNullOrEmpty(sFileName)) + sFileName = "Localization.csv"; + + string FullFileName = EditorUtility.SaveFilePanel("Select CSV File", sPath, sFileName, "csv"); + //string FullFileName = EditorUtility.OpenFilePanel("Select CSV, XLS or XLSX File", sFileName, "csv;*.xls;*.xlsx"); + + if (!string.IsNullOrEmpty(FullFileName)) + { + Prop_LocalFileName.stringValue = TryMakingPathRelativeToProject(FullFileName); + } + }*/ + GUILayout.EndHorizontal(); + + //--[ Find current extension ]--------------- + eLocalSpreadsheeet CurrentExtension = eLocalSpreadsheeet.NONE; + //string FileNameLower = Prop_LocalFileName.stringValue.ToLower(); + /*if (FileNameLower.EndsWith(".csv")) */CurrentExtension = eLocalSpreadsheeet.CSV; + /*if (FileNameLower.EndsWith(".xls")) CurrentExtension = eLocalSpreadsheeet.XLS; + if (FileNameLower.EndsWith(".xlsx")) CurrentExtension = eLocalSpreadsheeet.XLSX;*/ + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + switch (CurrentExtension) + { + case eLocalSpreadsheeet.NONE : + case eLocalSpreadsheeet.CSV : + { + string FileTypesDesc = "Select or Drag any file of the following types:\n\n"; + FileTypesDesc+= "*.csv (Comma Separated Values)\n"; + FileTypesDesc+= "*.txt (CSV file renamed as txt)\n"; + //FileTypesDesc+= "\n*.xls (Excel 97-2003)"; + //FileTypesDesc+= "\n*.xlsx (Excel Open XML format)"; + EditorGUILayout.HelpBox(FileTypesDesc, MessageType.None); + } + break; + case eLocalSpreadsheeet.XLS : EditorGUILayout.HelpBox("Excel 97-2003", MessageType.None); break; + case eLocalSpreadsheeet.XLSX : EditorGUILayout.HelpBox("Excel Open XML format", MessageType.None); break; + } + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + GUILayout.EndVertical(); + + //--[ Allow Dragging files ]----------------- + if (GUILayoutUtility.GetLastRect().Contains (UnityEngine.Event.current.mousePosition) && IsValidDraggedLoadSpreadsheet()) + { + if (UnityEngine.Event.current.type == EventType.DragUpdated) + DragAndDrop.visualMode = DragAndDropVisualMode.Link; + + if (UnityEngine.Event.current.type == EventType.DragPerform) + { + mProp_Spreadsheet_LocalFileName.stringValue = TryMakingPathRelativeToProject( DragAndDrop.paths[0] ); + DragAndDrop.AcceptDrag(); + UnityEngine.Event.current.Use(); + } + } + + GUILayout.Space(10); + + OnGUI_Spreadsheet_Local_ImportExport( CurrentExtension, mProp_Spreadsheet_LocalFileName.stringValue ); + + //if (Application.platform == RuntimePlatform.OSXEditor) + + //-- CSV Separator ---------------- + GUI.changed = false; + var CSV_Separator = mProp_Spreadsheet_LocalCSVSeparator.stringValue; + if (string.IsNullOrEmpty (CSV_Separator)) + CSV_Separator = ","; + + GUILayout.Space(10); + GUILayout.BeginVertical("Box"); + GUILayout.BeginHorizontal(); + GUILayout.Label("Separator:"); + GUILayout.FlexibleSpace(); + + if (GUILayout.Toggle(CSV_Separator==",", "Comma(,)") && CSV_Separator!=",") + CSV_Separator = ","; + + GUILayout.FlexibleSpace(); + + if (GUILayout.Toggle(CSV_Separator==";", "Semicolon(;)") && CSV_Separator!=";") + CSV_Separator = ";"; + + GUILayout.FlexibleSpace(); + + if (GUILayout.Toggle(CSV_Separator=="\t", "TAB(\\t)") && CSV_Separator!="\t") + CSV_Separator = "\t"; + + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + //--[ Encoding ]--------------- + var encodings = Encoding.GetEncodings ().OrderBy(e=>e.Name).ToArray(); + var encodingNames = encodings.Select(e=>e.Name).ToArray(); + + int idx = Array.IndexOf (encodingNames, mProp_Spreadsheet_LocalCSVEncoding.stringValue); + if (idx == -1) + idx = Array.IndexOf (encodingNames, "utf-8"); + EditorGUIUtility.labelWidth = 80; + + idx = EditorGUILayout.Popup ("Encoding:", idx, encodingNames); + if (GUILayout.Button("Default", GUILayout.ExpandWidth(false))) + idx = Array.IndexOf (encodingNames, "utf-8"); + + if (idx>=0 && mProp_Spreadsheet_LocalCSVEncoding.stringValue != encodings [idx].Name) + mProp_Spreadsheet_LocalCSVEncoding.stringValue = encodings [idx].Name; + GUILayout.EndHorizontal(); + + GUILayout.EndVertical(); + + if (GUI.changed) + { + mProp_Spreadsheet_LocalCSVSeparator.stringValue = CSV_Separator; + } + + GUILayout.Space(10); + EditorGUILayout.HelpBox("On some Mac OS, there is a Unity Bug that makes the IDE crash when selecting a CSV file in the Open/Save File Dialog.\nJust by clicking the file, unity tries to preview the content and crashes.\n\nIf any of your the team members use Mac, its adviced to import/export the CSV Files with TXT extension.", MessageType.Warning); + GUILayout.Space(10); + + OnGUI_ShowMsg(); + } + + bool IsValidDraggedLoadSpreadsheet() + { + if (DragAndDrop.paths==null || DragAndDrop.paths.Length!=1) + return false; + + string sPath = DragAndDrop.paths[0].ToLower(); + if (sPath.EndsWith(".csv")) return true; + if (sPath.EndsWith(".txt")) return true; + //if (sPath.EndsWith(".xls")) return true; + //if (sPath.EndsWith(".xlsx")) return true; + + /*int iChar = sPath.LastIndexOfAny( "/\\.".ToCharArray() ); + if (iChar<0 || sPath[iChar]!='.') + return true; + return false;*/ + return false; + } + + string TryMakingPathRelativeToProject( string FileName ) + { + string ProjectPath = Application.dataPath.ToLower(); + string FileNameLower = FileName.ToLower(); + + if (FileNameLower.StartsWith(ProjectPath)) + FileName = FileName.Substring(ProjectPath.Length+1); + else + if (FileNameLower.StartsWith("assets/")) + FileName = FileName.Substring("assets/".Length); + return FileName; + } + + void OnGUI_Spreadsheet_Local_ImportExport( eLocalSpreadsheeet CurrentExtension, string File ) + { + GUI.enabled = CurrentExtension!=eLocalSpreadsheeet.NONE; + + GUILayout.BeginHorizontal(); + GUILayout.Space(10); + + GUI.backgroundColor = Color.Lerp(Color.gray, Color.white, 0.5f); + eSpreadsheetUpdateMode Mode = SynchronizationButtons("Import"); + if ( Mode!= eSpreadsheetUpdateMode.None) + Import_Local(File, CurrentExtension, Mode); + + GUILayout.FlexibleSpace(); + + GUI.backgroundColor = Color.Lerp(Color.gray, Color.white, 0.5f); + Mode = SynchronizationButtons("Export", true); + if ( Mode != eSpreadsheetUpdateMode.None) + Export_Local(File, CurrentExtension, Mode); + + GUILayout.Space(10); + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + + GUILayout.BeginVertical(); + EditorGUIUtility.labelWidth += 10; + EditorGUILayout.PropertyField(mProp_Spreadsheet_SpecializationAsRows, new GUIContent("Show Specializations as Rows", "true: Make each specialization a separate row (e.g. Term[VR]..., Term[Touch]....\nfalse: Merge specializations into same cell separated by [i2s_XXX]")); + EditorGUILayout.PropertyField(mProp_Spreadsheet_SortRows, new GUIContent("Sort Rows", "true: Sort each term by its name....\nfalse: Keep the terms order")); + EditorGUIUtility.labelWidth -= 10; + GUILayout.EndVertical(); + GUILayout.EndHorizontal(); + + + GUI.enabled = true; + } + + void Import_Local( string File, eLocalSpreadsheeet CurrentExtension, eSpreadsheetUpdateMode UpdateMode ) + { + try + { + serializedObject.ApplyModifiedProperties(); + serializedObject.Update(); + ClearErrors(); + + if (string.IsNullOrEmpty(File)) + File = Application.dataPath + "/Localization.csv"; + else + if (!Path.IsPathRooted(File)) + File = string.Concat(Application.dataPath, "/", File); + + // On Mac there is an issue with previewing CSV files, so its forced to only TXT + if (Application.platform == RuntimePlatform.OSXEditor) + File = EditorUtility.OpenFilePanel("Select a CSV file renamed as TXT", File, "txt"); + else + File = EditorUtility.OpenFilePanel("Select a CSV file or a CSV file renamed as TXT", File, "csv;*.txt"); + //File = EditorUtility.OpenFilePanel("Select CSV, XLS or XLSX File", File, "csv;*.xls;*.xlsx"); + if (!string.IsNullOrEmpty(File)) + { + mLanguageSource.Spreadsheet_LocalFileName = TryMakingPathRelativeToProject(File); + switch (CurrentExtension) + { + case eLocalSpreadsheeet.CSV : Import_CSV(File, UpdateMode); break; + } + ParseTerms(true, false, true); + EditorUtility.SetDirty (target); + AssetDatabase.SaveAssets(); + } + } + catch (Exception ex) + { + ShowError("Unable to import file"); + Debug.LogError(ex.Message); + } + } + + void Import_CSV( string FileName, eSpreadsheetUpdateMode UpdateMode ) + { + LanguageSourceData source = GetSourceData(); + var encoding = Encoding.GetEncoding (mProp_Spreadsheet_LocalCSVEncoding.stringValue); + if (encoding == null) + encoding = Encoding.UTF8; + string CSVstring = LocalizationReader.ReadCSVfile (FileName, encoding); + + char Separator = mProp_Spreadsheet_LocalCSVSeparator.stringValue.Length>0 ? mProp_Spreadsheet_LocalCSVSeparator.stringValue[0] : ','; + string sError = source.Import_CSV( string.Empty, CSVstring, UpdateMode, Separator); + if (!string.IsNullOrEmpty(sError)) + ShowError(sError); + + mSelectedCategories = source.GetCategories(); + } + + void Export_Local( string File, eLocalSpreadsheeet CurrentExtension, eSpreadsheetUpdateMode UpdateMode ) + { + try + { + serializedObject.ApplyModifiedProperties(); + serializedObject.Update(); + ClearErrors(); + + string sPath = string.Empty; + if (!Path.IsPathRooted(File)) + File = string.Concat(Application.dataPath, "/", File); + + try { + sPath = Path.GetDirectoryName(File); + } + catch( Exception){} + + if (string.IsNullOrEmpty(sPath)) + sPath = Application.dataPath + "/"; + + File = Path.GetFileName(File); + if (string.IsNullOrEmpty(File)) + File = "Localization.csv"; + + if (Application.platform == RuntimePlatform.OSXEditor) + File = EditorUtility.SaveFilePanel("Select a CSV file renamed as TXT", sPath, File, "txt"); + else + File = EditorUtility.SaveFilePanel("Select a CSV or TXT file", sPath, File, "csv;*.txt"); + if (!string.IsNullOrEmpty(File)) + { + mLanguageSource.Spreadsheet_LocalFileName = TryMakingPathRelativeToProject(File); + + char Separator = mProp_Spreadsheet_LocalCSVSeparator.stringValue.Length>0 ? mProp_Spreadsheet_LocalCSVSeparator.stringValue[0] : ','; + var encoding = Encoding.GetEncoding (mProp_Spreadsheet_LocalCSVEncoding.stringValue); + if (encoding == null) + encoding = Encoding.UTF8; + + switch (CurrentExtension) + { + case eLocalSpreadsheeet.CSV : Export_CSV(File, UpdateMode, Separator, encoding); break; + } + } + } + catch (Exception) + { + ShowError("Unable to export file\nCheck it is not READ-ONLY and that\nits not opened in an external viewer"); + } + } + + public void Export_CSV( string FileName, eSpreadsheetUpdateMode UpdateMode, char Separator, Encoding encoding ) + { + LanguageSourceData source = GetSourceData(); + + string CSVstring = source.Export_CSV(null, Separator, mProp_Spreadsheet_SpecializationAsRows.boolValue, mProp_Spreadsheet_SortRows.boolValue); + File.WriteAllText (FileName, CSVstring, encoding); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Spreadsheet_Local.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Spreadsheet_Local.cs.meta new file mode 100644 index 00000000..dd66d4c6 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Spreadsheet_Local.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: fdca66efafe784661b464934cacff065 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Terms.cs b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Terms.cs new file mode 100644 index 00000000..8063255e --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Terms.cs @@ -0,0 +1,840 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + public partial class LocalizationEditor + { + #region Variables + + Vector2 mScrollPos_Keys = Vector2.zero; + + public static string mKeyToExplore; // Key that should show all the language details + static string KeyList_Filter = ""; + float mRowSize=-1; + float ScrollHeight; + float mTermList_MaxWidth = -1; + + public static List mSelectedKeys = new List(); // Selected Keys in the list of mParsedKeys + public static List mSelectedCategories = new List(); + + + public enum eFlagsViewKeys + { + Used = 1<<1, + Missing = 1<<2, + NotUsed = 1<<3, + Untranslated = 1<<4, + } + public static int mFlagsViewKeys = (int)eFlagsViewKeys.Used | (int)eFlagsViewKeys.NotUsed; + + public static string mTermsList_NewTerm; + Rect mKeyListFilterRect; + + #endregion + + #region GUI Key List + + float ExpandedViewHeight; + float TermsListHeight; + + void OnGUI_KeysList(bool AllowExpandKey = true, float Height = 300.0f, bool ShowTools=true) + { + ///if (mTermList_MaxWidth<=0) + CalculateTermsListMaxWidth(); + + //--[ List Filters ]-------------------------------------- + + // The ID of this control is registered here to avoid losing focus when the terms list grows in the scrollbox + // This control is drawn later on + int KeyListFilterID = GUIUtility.GetControlID( FocusType.Keyboard ); + + OnGUI_ShowMsg(); + + GUILayout.BeginHorizontal(); + GUIStyle bstyle = new GUIStyle ("toolbarbutton"); + bstyle.fontSize = 15; + if (GUILayout.Button (new GUIContent("\u21bb", "Parse Scene and update terms list with missing and unused terms"), bstyle, GUILayout.Width(25))) + EditorApplication.update += DoParseTermsInCurrentSceneAndScripts; + if (GUILayout.Button(new GUIContent("\u21ea", "Refresh the translation of all Localize objects"), bstyle, GUILayout.Width(25))) + CallLocalizeAll(); + + GUILayout.Space (1); + + var oldFlags = mFlagsViewKeys; + mFlagsViewKeys = OnGUI_FlagToogle("Used","Shows All Terms referenced in the parsed scenes", mFlagsViewKeys, (int)eFlagsViewKeys.Used); + mFlagsViewKeys = OnGUI_FlagToogle("Not Used", "Shows all Terms from the Source that are not been used", mFlagsViewKeys, (int)eFlagsViewKeys.NotUsed); + mFlagsViewKeys = OnGUI_FlagToogle("Missing","Shows all Terms Used but not defined in the Source", mFlagsViewKeys, (int)eFlagsViewKeys.Missing); + + mFlagsViewKeys = OnGUI_FlagToogle("Untranslated", "Shows all Terms that were not translated to any language", mFlagsViewKeys, (int)eFlagsViewKeys.Untranslated); + + if (oldFlags!=mFlagsViewKeys) + ScheduleUpdateTermsToShowInList(); + + OnGUI_SelectedCategories(); + + GUILayout.EndHorizontal(); + + /*//if (UnityEngine.Event.current.type == EventType.Repaint) + TermsListHeight = Screen.height - 400; + Debug.Log(UnityEngine.Event.current.type + " " + TermsListHeight + " " + Screen.height + " " + GUILayoutUtility.GetLastRect().yMax); + + //TermsListHeight = Mathf.Min(Screen.height*0.5f, TermsListHeight); + mScrollPos_Keys = GUILayout.BeginScrollView(mScrollPos_Keys, false, false, "horizontalScrollbar", "verticalScrollbar", LocalizeInspector.GUIStyle_OldTextArea, GUILayout.Height(TermsListHeight)); + for (int i = 0; i < 1000; ++i) + GUILayout.Label("ahhh" + i); + GUILayout.EndScrollView(); + + return;*/ + TermsListHeight = Mathf.Min(Screen.height*0.5f, TermsListHeight); + + //--[ Keys List ]----------------------------------------- + GUI.backgroundColor = Color.Lerp(GUITools.LightGray, Color.white, 0.5f); + mScrollPos_Keys = GUILayout.BeginScrollView( mScrollPos_Keys, false, false, "horizontalScrollbar", "verticalScrollbar", LocalizeInspector.GUIStyle_OldTextArea ,GUILayout.Height(TermsListHeight)/*GUILayout.MinHeight(Height), GUILayout.MaxHeight(Screen.height), GUILayout.ExpandHeight(true)*/); + GUI.backgroundColor = Color.white; + + bool bAnyValidUsage = false; + + mRowSize = EditorStyles.toolbar.fixedHeight; + if (UnityEngine.Event.current!=null && UnityEngine.Event.current.type == EventType.Layout) + ScrollHeight = mScrollPos_Keys.y; + + float YPosMin = -ScrollHeight; + int nSkip = 0; + int nDraw = 0; + + if (mShowableTerms.Count == 0 && UnityEngine.Event.current.type == EventType.Layout) + UpdateTermsToShownInList (); + + float SkipSize = 0; + foreach (var parsedTerm in mShowableTerms) + { + string sKey = parsedTerm.Term; + string sCategory = parsedTerm.Category; + string FullKey = parsedTerm.FullTerm; + + int nUses = parsedTerm.Usage; + bAnyValidUsage = bAnyValidUsage | (nUses>=0); + + ShowTerm_termData = parsedTerm.termData; + + // Skip lines outside the view ----------------------- + YPosMin += mRowSize; + SkipSize += mRowSize; + float YPosMax = YPosMin + mRowSize; + bool isExpanded = AllowExpandKey && mKeyToExplore==FullKey; + if (!isExpanded && (YPosMax<-2*mRowSize || YPosMin>/*Screen.height*/TermsListHeight+mRowSize)) + { + if (YPosMin>TermsListHeight+mRowSize) + break; + + nSkip++; + continue; + } + nDraw++; + + //------------------------------------------------------ + + OnGUI_KeyHeader (sKey, sCategory, FullKey, nUses, YPosMin-mRowSize+mScrollPos_Keys.y); + + //--[ Key Details ]------------------------------- + + if (isExpanded) + { + GUILayout.Space(SkipSize); + SkipSize = 0; + OnGUI_KeyList_ShowKeyDetails(); + Rect rect = GUILayoutUtility.GetLastRect(); + if (rect.height>5) + ExpandedViewHeight = rect.height; + YPosMin += ExpandedViewHeight; + } + } + SkipSize += (mShowableTerms.Count - nDraw-nSkip) * mRowSize; + GUILayout.Space(SkipSize+2); + if (mSelectedCategories.Count < mParsedCategories.Count) + { + SkipSize += 25; + if (GUILayout.Button ("...", EditorStyles.label)) + { + mSelectedCategories.Clear (); + mSelectedCategories.AddRange (mParsedCategories); + } + } + OnGUI_KeysList_AddKey(); + + GUILayout.Label("", GUILayout.Width(mTermList_MaxWidth+10+30), GUILayout.Height(1)); + + GUILayout.EndScrollView(); + + TermsListHeight = YPosMin + mRowSize + 25;//SkipSize+25; + + //Rect ListRect = GUILayoutUtility.GetLastRect(); + //if (ListRect.height>5) + // TermsListHeight = ListRect.height; + //Debug.Log(nDraw + " " + nSkip + " " + Screen.height + " " + TermsListHeight); + + OnGUI_Keys_ListSelection( KeyListFilterID ); // Selection Buttons + +// if (!bAnyValidUsage) +// EditorGUILayout.HelpBox("Use (Tools\\Parse Terms) to find how many times each of the Terms are used", UnityEditor.MessageType.Info); + + if (ShowTools) + { + GUILayout.BeginHorizontal(); + GUI.enabled = mSelectedKeys.Count>0 || !string.IsNullOrEmpty(mKeyToExplore); + if (TestButton (eTest_ActionType.Button_AddSelectedTerms, new GUIContent("Add Terms", "Add terms to Source"), "Button", GUITools.DontExpandWidth)) AddTermsToSource(); + if (TestButton (eTest_ActionType.Button_RemoveSelectedTerms, new GUIContent("Remove Terms", "Remove Terms from Source"), "Button", GUITools.DontExpandWidth)) RemoveTermsFromSource(); + + GUILayout.FlexibleSpace (); + + if (GUILayout.Button ("Change Category")) OpenTool_ChangeCategoryOfSelectedTerms(); + GUI.enabled = true; + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace (); + bool newBool = GUILayout.Toggle(mLanguageSource.CaseInsensitiveTerms, "Case Insensitive Terms"); + if (newBool != mLanguageSource.CaseInsensitiveTerms) + { + mProp_CaseInsensitiveTerms.boolValue = newBool; + } + GUILayout.FlexibleSpace (); + GUILayout.EndHorizontal(); + } + //Debug.Log ("Draw: " + nDraw + " Skip: " + nSkip); + } + + static void ScheduleUpdateTermsToShowInList() + { + if (!mUpdateShowTermIsScheduled) + { + EditorApplication.update += UpdateTermsToShownInList; + mUpdateShowTermIsScheduled = true; + } + } + static bool mUpdateShowTermIsScheduled; + static void UpdateTermsToShownInList() + { + EditorApplication.update -= UpdateTermsToShownInList; + mUpdateShowTermIsScheduled = false; + + mShowableTerms.Clear (); + mSelectedCategories.Sort(); + foreach (KeyValuePair kvp in mParsedTerms) + { + ParsedTerm parsedTerm = kvp.Value; + if (ShouldShowTerm (parsedTerm.Term, parsedTerm.Category, parsedTerm.Usage, parsedTerm)) + mShowableTerms.Add(parsedTerm); + } + GUITools.RepaintInspectors(); + GUITools.ScheduleRepaintInspectors(); + } + + void OnGUI_KeyHeader (string sKey, string sCategory, string FullKey, int nUses, float YPosMin) + { + //--[ Toggle ]--------------------- + GUI.Box(new Rect(2, YPosMin+2, 18, mRowSize), "", "Toolbar"); + OnGUI_SelectableToogleListItem (new Rect(2, YPosMin+3, 15, mRowSize), FullKey, ref mSelectedKeys, "OL Toggle"); + + bool bEnabled = mSelectedKeys.Contains (FullKey); + //--[ Number of Objects using this key ]--------------------- + if (nUses >= 0) + { + if (nUses == 0) + { + GUI.color = Color.Lerp (Color.gray, Color.white, 0.5f); + GUI.Label (new Rect(20, YPosMin+2, 30, mRowSize), nUses.ToString (), "toolbarbutton"); + } + else + { + if (GUI.Button(new Rect(20, YPosMin + 2, 30, mRowSize), nUses.ToString(), "toolbarbutton")) + { + List selection = new List(mSelectedKeys); + if (!selection.Contains(FullKey)) + selection.Add(FullKey); + + List selGOs = new List(); + for (int i=0; i 0) + Selection.objects = selGOs.ToArray(); + else + ShowWarning("The selected Terms are not used in this Scene. Try opening other scenes"); + } + } + } + else + { + GUI.color = Color.Lerp (Color.red, Color.white, 0.6f); + if (GUI.Button (new Rect(20, YPosMin+2, 30, mRowSize), "", "toolbarbutton")) + { + mCurrentToolsMode = eToolsMode.Parse; + mCurrentViewMode = eViewMode.Tools; + } + } + GUI.color = Color.white; + + TermData termData = ShowTerm_termData!=null ? ShowTerm_termData : mLanguageSource.GetTermData (FullKey); + bool bKeyIsMissing = termData == null; + float MinX = 50; + if (bKeyIsMissing) + { + Rect rect = new Rect(50, YPosMin+2, mRowSize, mRowSize+2); + GUITools.DrawSkinIcon(rect, "CN EntryWarnIcon", "CN EntryWarn"); + GUI.Label (rect, new GUIContent ("", "This term is used in the scene, but its not localized in the Language Source")); + MinX += rect.width; + } + else MinX += 3; + + float listWidth = Mathf.Max(EditorGUIUtility.currentViewWidth / EditorGUIUtility.pixelsPerPoint, mTermList_MaxWidth); + Rect rectKey = new Rect(MinX, YPosMin+2, listWidth-MinX, mRowSize); + if (sCategory != LanguageSourceData.EmptyCategory) + rectKey.width -= 130; + if (mKeyToExplore == FullKey) + { + GUI.backgroundColor = Color.Lerp (Color.blue, Color.white, 0.8f); + if (GUI.Button (rectKey, new GUIContent (sKey, EditorStyles.foldout.onNormal.background), LocalizeInspector.GUIStyle_OldTextArea)) + { + mKeyToExplore = string.Empty; + ScheduleUpdateTermsToShowInList(); + ClearErrors (); + } + GUI.backgroundColor = Color.white; + } + else + { + GUIStyle LabelStyle = EditorStyles.label; + if (!bKeyIsMissing && !TermHasAllTranslations (mLanguageSource, termData)) + { + LabelStyle = new GUIStyle (EditorStyles.label); + LabelStyle.fontStyle = FontStyle.Italic; + GUI.color = Color.Lerp (Color.white, Color.yellow, 0.5f); + } + if (!bEnabled) + GUI.contentColor = Color.Lerp (Color.gray, Color.white, 0.3f); + if (GUI.Button (rectKey, sKey, LabelStyle)) + { + SelectTerm (FullKey); + ClearErrors (); + } + if (!bEnabled) + GUI.contentColor = Color.white; + GUI.color = Color.white; + } + //--[ Category ]-------------------------- + if (sCategory != LanguageSourceData.EmptyCategory) + { + if (mKeyToExplore == FullKey) + { + rectKey.x = listWidth - 100-38-20; + rectKey.width = 130; + if (GUI.Button (rectKey, sCategory, EditorStyles.toolbarButton)) + OpenTool_ChangeCategoryOfSelectedTerms (); + } + else + { + GUIStyle stl = new GUIStyle(EditorStyles.miniLabel); + stl.alignment = TextAnchor.MiddleRight; + rectKey.width = 130;//EditorStyles.miniLabel.CalcSize(new GUIContent(sCategory)).x; + rectKey.x = listWidth - rectKey.width - 38-20; + + if (GUI.Button (rectKey, sCategory, stl)) + { + SelectTerm (FullKey); + ClearErrors (); + } + } + } + } + + + void CalculateTermsListMaxWidth() + { + mTermList_MaxWidth = EditorGUIUtility.currentViewWidth / EditorGUIUtility.pixelsPerPoint - 120; + /*float maxWidth = Screen.width / 18; + foreach (KeyValuePair kvp in mParsedTerms) + { + var txt = kvp.Key; + if (txt.Length > 100) + txt = txt.Substring(0, 100); + var size = EditorStyles.label.CalcSize(new GUIContent(txt)); + mTermList_MaxWidth = Mathf.Max (mTermList_MaxWidth, size.x); + }*/ + } + + bool TermHasAllTranslations( LanguageSourceData source, TermData data ) + { + if (source==null) source = LocalizationManager.Sources[0]; + for (int i=0, imax=data.Languages.Length; ii ? source.mLanguages[i].IsEnabled() : true; + if (string.IsNullOrEmpty(data.Languages[i]) && isLangEnabled) + return false; + } + return true; + } + + void OnGUI_KeysList_AddKey() + { + GUILayout.BeginHorizontal(); + GUI.color = Color.Lerp(Color.gray, Color.white, 0.5f); + bool bWasEnabled = mTermsList_NewTerm!=null; + bool bEnabled = !GUILayout.Toggle (!bWasEnabled, "+", EditorStyles.toolbarButton, GUILayout.Width(30)); + GUI.color = Color.white; + + if (bWasEnabled && !bEnabled) mTermsList_NewTerm = null; + if (!bWasEnabled && bEnabled) mTermsList_NewTerm = string.Empty; + + if (bEnabled) + { + GUILayout.BeginHorizontal(EditorStyles.toolbar); + mTermsList_NewTerm = EditorGUILayout.TextField(mTermsList_NewTerm, EditorStyles.toolbarTextField, GUILayout.ExpandWidth(true)); + GUILayout.EndHorizontal(); + + LanguageSourceData.ValidateFullTerm( ref mTermsList_NewTerm ); + if (string.IsNullOrEmpty(mTermsList_NewTerm) || mLanguageSource.ContainsTerm(mTermsList_NewTerm) || mTermsList_NewTerm=="-") + GUI.enabled = false; + + if (TestButton (eTest_ActionType.Button_AddTerm_InTermsList, "Create Key", "toolbarbutton", GUILayout.ExpandWidth(false))) + { + AddLocalTerm(mTermsList_NewTerm); + SelectTerm( mTermsList_NewTerm ); + ClearErrors(); + mTermsList_NewTerm = null; + SetAllTerms_When_InferredTerms_IsInSource (); + } + GUI.enabled = true; + } + GUILayout.EndHorizontal(); + } + + void OpenTool_ChangeCategoryOfSelectedTerms() + { + mCurrentViewMode = eViewMode.Tools; + mCurrentToolsMode = eToolsMode.Categorize; + if (!string.IsNullOrEmpty(mKeyToExplore) && !mSelectedKeys.Contains(mKeyToExplore)) + mSelectedKeys.Add(mKeyToExplore); + mSelectedKeys.Sort(); + } + + void OnGUI_SelectedCategories() + { + if (mParsedCategories.Count == 0) + return; + + string text = "Categories"; + if (mSelectedCategories.Count() == 0) + text = "Nothing"; + else + if (mSelectedCategories.Count() == mParsedCategories.Count) + text = "Everything"; + else + text = mSelectedCategories.Count + " categories"; + + if (GUILayout.Button(new GUIContent(text), "toolbarbutton", GUILayout.Width(100))) + { + var menu = new GenericMenu(); + + menu.AddItem(new GUIContent("Everything"), false, () => + { + mSelectedCategories.Clear(); + mSelectedCategories.AddRange(mParsedCategories); + ScheduleUpdateTermsToShowInList(); + }); + menu.AddItem(new GUIContent("Nothing"), false, () => + { + mSelectedCategories.Clear(); + ScheduleUpdateTermsToShowInList(); + }); + menu.AddSeparator(""); + + var parsedList = mParsedCategories.OrderBy(x=>x).ToList(); + for (int i=0, imax=parsedList.Count; ix.StartsWith(categoryRoot))), () => + { + var CatHeader = category + "/"; + if (mSelectedCategories.Contains(category)) + { + mSelectedCategories.Remove(category); + + if (isHeader) + { + mSelectedCategories.RemoveAll(x => x.StartsWith(CatHeader)); + } + } + else + { + mSelectedCategories.Add(category); + if (isHeader) + { + mSelectedCategories.AddRange( parsedList.Where(x=>x.StartsWith(CatHeader))); + } + } + ScheduleUpdateTermsToShowInList(); + }); + if (isHeader) + { + menu.AddSeparator(category+"/"); + } + } + + menu.ShowAsContext(); + } + } + + void SaveSelectedCategories() + { + if (mSelectedCategories.Count == 0) { + EditorPrefs.DeleteKey ("I2 CategoryFilter"); + } else { + var data = string.Join(",", mSelectedCategories.ToArray()); + EditorPrefs.SetString ("I2 CategoryFilter", data); + } + } + + void LoadSelectedCategories() + { + var data = EditorPrefs.GetString ("I2 CategoryFilter", null); + if (!string.IsNullOrEmpty(data)) + { + mSelectedCategories.Clear (); + mSelectedCategories.AddRange( data.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)); + } + } + + + // Bottom part of the Key list (buttons: All, None, Used,... to select the keys) + void OnGUI_Keys_ListSelection ( int KeyListFilterID ) + { + GUILayout.BeginHorizontal( "toolbarbutton" ); + + if (TestButton( eTest_ActionType.Button_SelectTerms_All, new GUIContent( "All", "Selects All Terms in the list" ), "toolbarbutton", GUILayout.ExpandWidth( false ) )) + { + mSelectedKeys.Clear(); + foreach (var kvp in mParsedTerms) + if (ShouldShowTerm( kvp.Value.Term, kvp.Value.Category, kvp.Value.Usage )) + mSelectedKeys.Add( kvp.Key ); + } + if (GUILayout.Button( new GUIContent( "None", "Clears the selection" ), "toolbarbutton", GUILayout.ExpandWidth( false ) )) { mSelectedKeys.Clear(); } + GUILayout.Space( 5 ); + + GUI.enabled = (mFlagsViewKeys & (int)eFlagsViewKeys.Used)>1; + if (TestButton(eTest_ActionType.Button_SelectTerms_Used, new GUIContent( "Used", "Selects All Terms referenced in the parsed scenes" ), "toolbarbutton", GUILayout.ExpandWidth( false ) )) + { + mSelectedKeys.Clear(); + foreach (var kvp in mParsedTerms) + if (kvp.Value.Usage > 0 && ShouldShowTerm( kvp.Value.Term, kvp.Value.Category, kvp.Value.Usage )) + mSelectedKeys.Add( kvp.Key ); + } + GUI.enabled = (mFlagsViewKeys & (int)eFlagsViewKeys.NotUsed)>1; + if (GUILayout.Button( new GUIContent( "Not Used", "Selects all Terms from the Source that are not been used" ), "toolbarbutton", GUILayout.ExpandWidth( false ) )) + { + mSelectedKeys.Clear(); + foreach (var kvp in mParsedTerms) + if (kvp.Value.Usage == 0 && ShouldShowTerm( kvp.Value.Term, kvp.Value.Category, kvp.Value.Usage )) + mSelectedKeys.Add( kvp.Key ); + } + + GUI.enabled = (mFlagsViewKeys & (int)eFlagsViewKeys.Missing)>1; + if (TestButton(eTest_ActionType.Button_SelectTerms_Missing, new GUIContent( "Missing", "Selects all Terms Used but not defined in the Source" ), "toolbarbutton", GUILayout.ExpandWidth( false ) )) + { + mSelectedKeys.Clear(); + foreach (var kvp in mParsedTerms) + if (!mLanguageSource.ContainsTerm( kvp.Key ) && ShouldShowTerm( kvp.Value.Term, kvp.Value.Category, kvp.Value.Usage )) + mSelectedKeys.Add( kvp.Key ); + } + + GUI.enabled = ((mFlagsViewKeys & (int)eFlagsViewKeys.Untranslated) > 1); + if (GUILayout.Button(new GUIContent("Untranslated", "Selects all Terms from the Source that are not translated to any language"), "toolbarbutton", GUILayout.ExpandWidth(false))) + { + mSelectedKeys.Clear(); + foreach (var kvp in mParsedTerms) + if (kvp.Value.termData.Languages.All(o => string.IsNullOrEmpty(o)) && ShouldShowTerm(kvp.Value.Term, kvp.Value.Category, kvp.Value.Usage)) + mSelectedKeys.Add(kvp.Key); + } + + GUI.enabled = true; + EditorGUI.BeginChangeCheck(); + + // Terms Filter + { + //KeyList_Filter = EditorGUILayout.TextField(KeyList_Filter, GUI.skin.GetStyle(GUITools.Style_ToolbarSearchTextField), GUILayout.ExpandWidth(true)); + GUILayout.Label( "", GUILayout.ExpandWidth( true ) ); + mKeyListFilterRect = GUILayoutUtility.GetLastRect(); + mKeyListFilterRect.xMax += 4; + + KeyList_Filter = GUITools.TextField( mKeyListFilterRect, KeyList_Filter, 255, GUI.skin.GetStyle( GUITools.Style_ToolbarSearchTextField ), KeyListFilterID ); + } + + + + if (GUILayout.Button( string.Empty, string.IsNullOrEmpty( KeyList_Filter ) ? GUITools.Style_ToolbarSearchCancelButtonEmpty : GUITools.Style_ToolbarSearchCancelButton, GUILayout.ExpandWidth( false ) )) + { + KeyList_Filter = string.Empty; + EditorApplication.update += RepaintScene; + GUI.FocusControl( "" ); + } + + string filterHelp = "Fiter Options:\ntext - shows all key/categories matching text\nc text - shows all terms of the text category\nf text - show terms having 'text' in their translations"; + GUILayout.Space(-5); + GUI.contentColor = new Color(1, 1, 1, 0.5f); + GUILayout.Label(new GUIContent(GUITools.Icon_Help.image, filterHelp), GUITools.DontExpandWidth); + GUI.contentColor = GUITools.White; + GUILayout.Space(-5); + + + + + if (EditorGUI.EndChangeCheck()) + { + mShowableTerms.Clear(); + GUI.changed = false; + } + + GUILayout.EndHorizontal(); + } + + + #endregion + + #region Filtering + + public bool ShouldShowTerm (string FullTerm) + { + ParsedTerm termData; + if (!mParsedTerms.TryGetValue(FullTerm, out termData)) + return false; + + return ShouldShowTerm (termData.Term, termData.Category, termData.Usage, termData); + } + + private static TermData ShowTerm_termData; + public static bool ShouldShowTerm (string Term, string Category, int nUses, ParsedTerm parsedTerm=null ) + { + if (!string.IsNullOrEmpty(Category) && !mSelectedCategories.Contains(Category)) + return false; + if (Term == "-") + return false; + + + var fullTerm = Term; + if (!string.IsNullOrEmpty(Category) && Category != LanguageSourceData.EmptyCategory) + fullTerm = Category + "/" + Term; + + if (parsedTerm != null && parsedTerm.termData != null) + ShowTerm_termData = parsedTerm.termData; + else + { + ShowTerm_termData = mLanguageSource.GetTermData (fullTerm); + if (parsedTerm!=null) + parsedTerm.termData = ShowTerm_termData; + } + + var filter = KeyList_Filter.Trim(); + bool useTranslation = filter.StartsWith("f ", StringComparison.OrdinalIgnoreCase); + if (useTranslation) + { + if (ShowTerm_termData == null) + return false; + + filter = filter.Substring(2).Trim(); + if (!string.IsNullOrEmpty(filter)) + { + bool hasFilter = false; + for (int i = 0; i < ShowTerm_termData.Languages.Length; ++i) + { + if (!string.IsNullOrEmpty(ShowTerm_termData.Languages[i]) + && StringContainsFilter(ShowTerm_termData.Languages[i], filter)) + { + hasFilter = true; + break; + } + + } + if (!hasFilter) + return false; + } + } + else + { + bool onlyCategory = filter.StartsWith("c ", StringComparison.OrdinalIgnoreCase); + if (onlyCategory) + filter = filter.Substring(2).Trim(); + + if (!string.IsNullOrEmpty(filter)) + { + bool matchesCategory = StringContainsFilter(Category, filter); + bool matchesName = !onlyCategory && StringContainsFilter(Term, filter); + + if (!matchesCategory && !matchesName) + return false; + } + } + + + bool bIsMissing = ShowTerm_termData == null; + bool hasTranslation = !bIsMissing && ShowTerm_termData.Languages.Any(o => !string.IsNullOrEmpty(o)); + + if ((mFlagsViewKeys & (int)eFlagsViewKeys.Untranslated) > 0) return !hasTranslation; + + if (nUses<0) return true; + + + if ((mFlagsViewKeys & (int)eFlagsViewKeys.Missing)>0 && bIsMissing) return true; + if ((mFlagsViewKeys & (int)eFlagsViewKeys.Missing)==0 && bIsMissing) return false; + + if ((mFlagsViewKeys & (int)eFlagsViewKeys.Used)>0 && nUses>0) return true; + if ((mFlagsViewKeys & (int)eFlagsViewKeys.NotUsed)>0 && nUses==0) return true; + + return false; + } + + static bool StringContainsFilter( string Term, string Filter ) + { + if (string.IsNullOrEmpty(Filter) || string.IsNullOrEmpty(Term)) + return true; + if (Term == "-") + return false; + Term = Term.ToLower(); + string[] Filters = Filter.ToLower().Split(";, ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); + for (int i = 0, imax = Filters.Length; i < imax; ++i) + if (Term.Contains(Filters[i])) + return true; + + return false; + } + + #endregion + + #region Add/Remove Keys to DB + + void AddTermsToSource() + { + if (!string.IsNullOrEmpty (mKeyToExplore) && !mSelectedKeys.Contains(mKeyToExplore)) + mSelectedKeys.Add (mKeyToExplore); + + for (int i=mSelectedKeys.Count-1; i>=0; --i) + { + string key = mSelectedKeys[i]; + + if (!ShouldShowTerm(key)) + continue; + + AddLocalTerm(key); + mSelectedKeys.RemoveAt(i); + } + SetAllTerms_When_InferredTerms_IsInSource (); + } + + void RemoveTermsFromSource() + { + if (mTestAction==eTest_ActionType.None && !EditorUtility.DisplayDialog("Confirm delete", "Are you sure you want to delete the selected terms", "Yes", "Cancel")) + return; + + if (!string.IsNullOrEmpty (mKeyToExplore) && !mSelectedKeys.Contains(mKeyToExplore)) + mSelectedKeys.Add (mKeyToExplore); + + for (int i=mSelectedKeys.Count-1; i>=0; --i) + { + string key = mSelectedKeys[i]; + + if (!ShouldShowTerm(key)) + continue; + + mLanguageSource.RemoveTerm(key); + RemoveParsedTerm(key); + mSelectedKeys.Remove(key); + } + + mKeyToExplore = string.Empty; + mTermList_MaxWidth = -1; + serializedObject.ApplyModifiedProperties(); + mLanguageSource.Editor_SetDirty(); + + EditorApplication.update += DoParseTermsInCurrentScene; + EditorApplication.update += RepaintScene; + } + + #endregion + + #region Select Objects in Current Scene + + + public static void SelectTerm( string Key, bool SwitchToKeysTab=false ) + { + GUI.FocusControl(null); + mKeyToExplore = Key; + mKeysDesc_AllowEdit = false; + if (SwitchToKeysTab) + mCurrentViewMode = eViewMode.Keys; + } + + + void SelectObjectsUsingKey( string Key ) + { + List SelectedObjs = FindObjectsUsingKey(Key); + + if (SelectedObjs.Count>0) + Selection.objects = SelectedObjs.ToArray(); + else + ShowWarning("The selected Terms are not used in this Scene. Try opening other scenes"); + } + + List FindObjectsUsingKey(string Key) + { + List SelectedObjs = new List(); + + Localize[] Locals = (Localize[])Resources.FindObjectsOfTypeAll(typeof(Localize)); + + if (Locals == null) + return SelectedObjs; + + for (int i = 0, imax = Locals.Length; i < imax; ++i) + { + Localize localize = Locals[i]; + if (localize == null || localize.gameObject == null || !GUITools.ObjectExistInScene(localize.gameObject)) + continue; + + string Term, SecondaryTerm; + localize.GetFinalTerms(out Term, out SecondaryTerm); + + if (Key == Term || Key == SecondaryTerm) + SelectedObjs.Add(localize.gameObject); + } + + return SelectedObjs; + } + + + #endregion + + + [MenuItem("Tools/I2 Localization/Refresh Localizations", false, 16)] + public static void CallLocalizeAll() + { + LocalizationManager.LocalizeAll(true); + HandleUtility.Repaint(); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Terms.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Terms.cs.meta new file mode 100644 index 00000000..9384003c --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Terms.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 9f0230a94fc864d5bb1f2261de16edce +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Terms_Description.cs b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Terms_Description.cs new file mode 100644 index 00000000..a9f8f2ac --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Terms_Description.cs @@ -0,0 +1,785 @@ +//#define UGUI +//#define NGUI + +using System; +using TMPro; +using UnityEditor; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace TEngine.Localization +{ + public partial class LocalizationEditor + { + #region Variables + internal static bool mKeysDesc_AllowEdit; + internal static string GUI_SelectedSpecialization + { + get{ + if (string.IsNullOrEmpty(mGUI_SelectedSpecialization)) + mGUI_SelectedSpecialization = EditorPrefs.GetString ("I2Loc Specialization", "Any"); + return mGUI_SelectedSpecialization; + } + set{ + if (value!=mGUI_SelectedSpecialization) + EditorPrefs.SetString ("I2Loc Specialization", value); + mGUI_SelectedSpecialization = value; + } + } + internal static string mGUI_SelectedSpecialization; + + internal static bool GUI_ShowDisabledLanguagesTranslation = true; + + internal static int mShowPlural = -1; + #endregion + + #region Key Description + + void OnGUI_KeyList_ShowKeyDetails() + { + GUI.backgroundColor = Color.Lerp(Color.blue, Color.white, 0.9f); + GUILayout.BeginVertical(LocalizeInspector.GUIStyle_OldTextArea, GUILayout.Height(1)); + OnGUI_Keys_Languages(mKeyToExplore, null); + + GUILayout.BeginHorizontal(); + if (TestButton(eTest_ActionType.Button_DeleteTerm, "Delete", "Button", GUILayout.ExpandWidth(true))) + { + if (mTestAction != eTest_ActionType.None || EditorUtility.DisplayDialog("Confirm delete", "Are you sure you want to delete term '" + mKeyToExplore + "'", "Yes", "Cancel")) + EditorApplication.update += DeleteCurrentKey; + } + + if (GUILayout.Button("Rename")) + { + mCurrentViewMode = eViewMode.Tools; + mCurrentToolsMode = eToolsMode.Merge; + if (!mSelectedKeys.Contains(mKeyToExplore)) + mSelectedKeys.Add(mKeyToExplore); + } + GUILayout.EndHorizontal(); + GUILayout.EndVertical(); + GUI.backgroundColor = Color.white; + } + + void DeleteTerm( string Term, bool updateStructures = true ) + { + mLanguageSource.RemoveTerm (Term); + RemoveParsedTerm(Term); + mSelectedKeys.Remove(Term); + + if (Term==mKeyToExplore) + mKeyToExplore = string.Empty; + + if (updateStructures) + { + UpdateParsedCategories(); + mTermList_MaxWidth = -1; + serializedObject.ApplyModifiedProperties(); + mLanguageSource.Editor_SetDirty(); + ScheduleUpdateTermsToShowInList(); + } + EditorApplication.update += RepaintScene; + } + + void RepaintScene() + { + EditorApplication.update -= RepaintScene; + Repaint(); + } + + void DeleteCurrentKey() + { + EditorApplication.update -= DeleteCurrentKey; + DeleteTerm (mKeyToExplore); + + mKeyToExplore = ""; + EditorApplication.update += DoParseTermsInCurrentScene; + } + + TermData AddLocalTerm( string Term, bool AutoSelect = true ) + { + var data = AddTerm(Term, AutoSelect); + if (data==null) + return null; + + mTermList_MaxWidth = -1; + serializedObject.ApplyModifiedProperties(); + mLanguageSource.Editor_SetDirty(); + return data; + } + + static TermData AddTerm(string Term, bool AutoSelect = true, eTermType termType = eTermType.Text) + { + if (Term == "-" || string.IsNullOrEmpty(Term)) + return null; + + Term = I2Utils.GetValidTermName(Term, true); + + TermData data = mLanguageSource.AddTerm(Term, termType); + GetParsedTerm(Term); + string sCategory = LanguageSourceData.GetCategoryFromFullTerm(Term); + mParsedCategories.Add(sCategory); + + if (AutoSelect) + { + if (!mSelectedKeys.Contains(Term)) + mSelectedKeys.Add(Term); + + if (!mSelectedCategories.Contains(sCategory)) + mSelectedCategories.Add(sCategory); + } + ScheduleUpdateTermsToShowInList(); + mLanguageSource.Editor_SetDirty(); + return data; + } + + // this method shows the key description and the localization to each language + public static TermData OnGUI_Keys_Languages( string Key, Localize localizeCmp, bool IsPrimaryKey=true ) + { + if (Key==null) + Key = string.Empty; + + TermData termdata = null; + + LanguageSourceData source = mLanguageSource; + if (localizeCmp != null && localizeCmp.Source != null) + source = localizeCmp.Source.SourceData; + + if (source==null) + source = LocalizationManager.GetSourceContaining(Key, false); + + if (source==null) + { + if (localizeCmp == null) + source = LocalizationManager.Sources[0]; + else + source = LocalizationManager.GetSourceContaining(IsPrimaryKey ? localizeCmp.SecondaryTerm : localizeCmp.Term); + } + + + if (string.IsNullOrEmpty(Key)) + { + EditorGUILayout.HelpBox( "Select a Term to Localize", MessageType.Info ); + return null; + } + + termdata = source.GetTermData(Key); + if (termdata==null && localizeCmp!=null) + { + var realSource = LocalizationManager.GetSourceContaining(Key, false); + if (realSource != null) + { + termdata = realSource.GetTermData(Key); + source = realSource; + } + } + if (termdata==null) + { + if (Key == "-") + return null; + EditorGUILayout.HelpBox( string.Format("Key '{0}' is not Localized or it is in a different Language Source", Key), MessageType.Error ); + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Add Term to Source")) + { + var termType = eTermType.Text; + if (localizeCmp!=null && localizeCmp.mLocalizeTarget != null) + { + termType = IsPrimaryKey ? localizeCmp.mLocalizeTarget.GetPrimaryTermType(localizeCmp) + : localizeCmp.mLocalizeTarget.GetSecondaryTermType(localizeCmp); + } + + AddTerm(Key, true, termType); + } + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + return null; + } + + //--[ Type ]---------------------------------- + if (localizeCmp==null) + { + GUILayout.BeginHorizontal(); + GUILayout.Label ("Type:", GUILayout.ExpandWidth(false)); + eTermType NewType = (eTermType)EditorGUILayout.EnumPopup(termdata.TermType, GUILayout.ExpandWidth(true)); + if (termdata.TermType != NewType) + termdata.TermType = NewType; + GUILayout.EndHorizontal(); + } + + + //--[ Description ]--------------------------- + + mKeysDesc_AllowEdit = GUILayout.Toggle(mKeysDesc_AllowEdit, "Description", EditorStyles.foldout, GUILayout.ExpandWidth(true)); + + if (mKeysDesc_AllowEdit) + { + string NewDesc = EditorGUILayout.TextArea( termdata.Description, Style_WrapTextField ); + if (NewDesc != termdata.Description) + { + termdata.Description = NewDesc; + source.Editor_SetDirty(); + } + } + else + EditorGUILayout.HelpBox( string.IsNullOrEmpty(termdata.Description) ? "No description" : termdata.Description, MessageType.Info ); + + OnGUI_Keys_Language_SpecializationsBar (termdata, source); + + OnGUI_Keys_Languages(Key, ref termdata, localizeCmp, IsPrimaryKey, source); + return termdata; + } + + static void OnGUI_Keys_Languages( string Key, ref TermData termdata, Localize localizeCmp, bool IsPrimaryKey, LanguageSourceData source ) + { + //--[ Languages ]--------------------------- + GUILayout.BeginVertical(LocalizeInspector.GUIStyle_OldTextArea, GUILayout.Height(1)); + + OnGUI_Keys_LanguageTranslations(Key, localizeCmp, IsPrimaryKey, ref termdata, source); + + if (termdata.TermType == eTermType.Text) + { + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (TestButton(eTest_ActionType.Button_Term_TranslateAll, "Translate All", "Button", GUILayout.Width(85))) + { + var termData = termdata; + GUITools.DelayedCall(() => TranslateLanguage( Key, termData, localizeCmp, source)); + GUI.FocusControl(string.Empty); + } + GUILayout.EndHorizontal(); + OnGUI_TranslatingMessage(); + } + GUILayout.EndVertical(); + } + + static void TranslateLanguage( string Key, TermData termdata, Localize localizeCmp, LanguageSourceData source) + { + ClearErrors(); + string mainText = localizeCmp == null ? LanguageSourceData.GetKeyFromFullTerm(Key) : localizeCmp.GetMainTargetsText(); + + for (int i = 0; i < source.mLanguages.Count; ++i) + if (source.mLanguages[i].IsEnabled() && string.IsNullOrEmpty(termdata.Languages[i])) + { + var langIdx = i; + var term = termdata; + var i2source = source; + Translate(mainText, ref termdata, source.mLanguages[i].Code, + (translation, error) => + { + if (error != null) + ShowError(error); + else + if (translation != null) + { + term.Languages[langIdx] = translation; //SetTranslation(langIdx, translation); + i2source.Editor_SetDirty(); + } + }, null); + } + } + + static void OnGUI_TranslatingMessage() + { + if (GoogleTranslation.IsTranslating()) + { + // Connection Status Bar + int time = (int)(Time.realtimeSinceStartup % 2 * 2.5); + string Loading = "Translating" + ".....".Substring(0, time); + GUI.color = Color.gray; + GUILayout.BeginHorizontal(LocalizeInspector.GUIStyle_OldTextArea); + GUILayout.Label(Loading, EditorStyles.miniLabel); + GUI.color = Color.white; + if (GUILayout.Button("Cancel", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) + { + GoogleTranslation.CancelCurrentGoogleTranslations(); + } + GUILayout.EndHorizontal(); + HandleUtility.Repaint (); + } + } + + static void OnGUI_Keys_Language_SpecializationsBar(TermData termData, LanguageSourceData source) + { + var activeSpecializations = termData.GetAllSpecializations(); + + GUILayout.BeginHorizontal(); + + var TabStyle = new GUIStyle(GUI.skin.FindStyle("dragtab")); + TabStyle.fixedHeight = 0; + + //var ss = GUI.skin.FindStyle("TL tab left"); + var TabOpenStyle = new GUIStyle(GUI.skin.FindStyle("minibuttonmid")); + TabOpenStyle.margin.right = -1; + var TabCloseStyle = new GUIStyle(EditorStyles.label); + //var TabCloseStyle = new GUIStyle(GUI.skin.FindStyle("TL tab right")); + TabCloseStyle.margin.left = -1; + TabCloseStyle.padding.left=4; + + //-- Specialization Tabs ----- + + var prevSpecialization = "Any"; + foreach (var specialization in SpecializationManager.Singleton.mSpecializations) + { + if (!activeSpecializations.Contains(specialization) && specialization != GUI_SelectedSpecialization) + continue; + + bool isActive = specialization == GUI_SelectedSpecialization; + var labelContent = new GUIContent(specialization, "Specialization of the main translation (i.e. variants that show only on specific platforms or devices)\nThis allows using 'tap' instead of 'click' for touch devices."); + + if (isActive && activeSpecializations.Count>1) + { + GUILayout.BeginHorizontal(TabOpenStyle); + GUILayout.Toggle(isActive, labelContent, TabStyle, GUILayout.Height(20), GUILayout.ExpandWidth(false)); + //GUILayout.Label(labelContent, TabOpenStyle); + if (specialization != "Any" && GUILayout.Button("x", TabCloseStyle, GUILayout.Width(15))) + { + termData.RemoveSpecialization(specialization); + GUI_SelectedSpecialization = prevSpecialization; + GUI.FocusControl(null); + } + GUILayout.EndHorizontal(); + } + else + if (GUILayout.Toggle(isActive, labelContent, TabStyle, GUILayout.Height(25), GUILayout.ExpandWidth(false)) && !isActive) + { + GUI_SelectedSpecialization = specialization; + GUI.FocusControl(null); + } + } + + + //-- Add new Specialization ----- + int newIndex = EditorGUILayout.Popup(-1, SpecializationManager.Singleton.mSpecializations, "DropDown", GUILayout.Width(20)); + if (newIndex>=0) + { + string newSpecialization = SpecializationManager.Singleton.mSpecializations[newIndex]; + if (!activeSpecializations.Contains(newSpecialization)) + { + for (int iLang = 0; iLang < source.mLanguages.Count; ++iLang) + { + string Translation = termData.GetTranslation(iLang, GUI_SelectedSpecialization, editMode: true); + termData.SetTranslation(iLang, Translation, GUI_SelectedSpecialization); + } + GUI_SelectedSpecialization = newSpecialization; + } + } + + GUILayout.FlexibleSpace(); + + + GUI_ShowDisabledLanguagesTranslation = GUILayout.Toggle(GUI_ShowDisabledLanguagesTranslation, new GUIContent("L", "Show Disabled Languages"), "Button", GUILayout.ExpandWidth(false)); + GUILayout.EndHorizontal(); + GUILayout.Space(-3); + } + + static void OnGUI_Keys_LanguageTranslations (string Key, Localize localizeCmp, bool IsPrimaryKey, ref TermData termdata, LanguageSourceData source) + { + bool IsSelect = UnityEngine.Event.current.type==EventType.MouseUp; + for (int i=0; i< source.mLanguages.Count; ++ i) + { + bool forcePreview = false; + bool isEnabledLanguage = source.mLanguages[i].IsEnabled(); + + if (!isEnabledLanguage) + { + if (!GUI_ShowDisabledLanguagesTranslation) + continue; + GUI.color = new Color(GUI.color.r, GUI.color.g, GUI.color.b, 0.35f); + } + GUILayout.BeginHorizontal(); + + if (GUILayout.Button(source.mLanguages[i].Name, EditorStyles.label, GUILayout.Width(100))) + forcePreview = true; + + + string Translation = termdata.GetTranslation(i, GUI_SelectedSpecialization, editMode:true); + if (Translation == null) Translation = string.Empty; + +// if (termdata.Languages[i] != termdata.Languages_Touch[i] && !string.IsNullOrEmpty(termdata.Languages[i]) && !string.IsNullOrEmpty(termdata.Languages_Touch[i])) +// GUI.contentColor = GUITools.LightYellow; + + if (termdata.TermType == eTermType.Text || termdata.TermType==eTermType.Child) + { + EditorGUI.BeginChangeCheck (); + string CtrName = "TranslatedText"+i; + GUI.SetNextControlName(CtrName); + + EditPluralTranslations (ref Translation, i, source.mLanguages[i].Code); + //Translation = EditorGUILayout.TextArea(Translation, Style_WrapTextField, GUILayout.Width(Screen.width - 260 - (autoTranslated ? 20 : 0))); + if (EditorGUI.EndChangeCheck ()) + { + termdata.SetTranslation(i, Translation, GUI_SelectedSpecialization); + source.Editor_SetDirty(); + forcePreview = true; + } + + if (localizeCmp!=null && + (forcePreview || /*GUI.changed || */GUI.GetNameOfFocusedControl()==CtrName && IsSelect)) + { + if (IsPrimaryKey && string.IsNullOrEmpty(localizeCmp.Term)) + { + localizeCmp.mTerm = Key; + } + + if (!IsPrimaryKey && string.IsNullOrEmpty(localizeCmp.SecondaryTerm)) + { + localizeCmp.mTermSecondary = Key; + } + + string PreviousLanguage = LocalizationManager.CurrentLanguage; + LocalizationManager.PreviewLanguage(source.mLanguages[i].Name); + if (forcePreview || IsSelect) + LocalizationManager.LocalizeAll(); + else + localizeCmp.OnLocalize(true); + LocalizationManager.PreviewLanguage(PreviousLanguage); + EditorUtility.SetDirty(localizeCmp); + } + GUI.contentColor = Color.white; + + //if (autoTranslated) + //{ + // if (GUILayout.Button(new GUIContent("\u2713"/*"A"*/,"Translated by Google Translator\nClick the button to approve the translation"), EditorStyles.toolbarButton, GUILayout.Width(autoTranslated ? 20 : 0))) + // { + // termdata.Flags[i] &= (byte)(byte.MaxValue ^ (byte)(GUI_SelectedSpecialization==0 ? TranslationFlag.AutoTranslated_Normal : TranslationFlag.AutoTranslated_Touch)); + // } + //} + + if (termdata.TermType == eTermType.Text) + { + if (TestButtonArg(eTest_ActionType.Button_Term_Translate, i, new GUIContent("T", "Translate"), EditorStyles.toolbarButton, GUILayout.Width(20))) + { + var termData = termdata; + var indx = i; + var key = Key; + GUITools.DelayedCall(()=>TranslateTerm(key, termData, source, indx)); + GUI.FocusControl(string.Empty); + } + } + } + else + { + string MultiSpriteName = string.Empty; + + if (termdata.TermType==eTermType.Sprite && Translation.EndsWith("]", StringComparison.Ordinal)) // Handle sprites of type (Multiple): "SpritePath[SpriteName]" + { + int idx = Translation.LastIndexOf("[", StringComparison.Ordinal); + int len = Translation.Length-idx-2; + MultiSpriteName = Translation.Substring(idx+1, len); + Translation = Translation.Substring(0, idx); + } + + Object Obj = null; + + // Try getting the asset from the References section + if (localizeCmp!=null) + Obj = localizeCmp.FindTranslatedObject(Translation); + if (Obj==null && source != null) + Obj = source.FindAsset(Translation); + + // If it wasn't in the references, Load it from Resources + if (Obj==null && localizeCmp==null) + Obj = ResourceManager.pInstance.LoadFromResources(Translation); + + Type ObjType = typeof(Object); + switch (termdata.TermType) + { + case eTermType.Font : ObjType = typeof(Font); break; + case eTermType.Texture : ObjType = typeof(Texture); break; + case eTermType.AudioClip : ObjType = typeof(AudioClip); break; + case eTermType.GameObject : ObjType = typeof(GameObject); break; + case eTermType.Sprite : ObjType = typeof(Sprite); break; + case eTermType.Material : ObjType = typeof(Material); break; + case eTermType.Mesh : ObjType = typeof(Mesh); break; +#if NGUI + case eTermType.UIAtlas : ObjType = typeof(UIAtlas); break; + case eTermType.UIFont : ObjType = typeof(UIFont); break; +#endif +#if TK2D + case eTermType.TK2dFont : ObjType = typeof(tk2dFont); break; + case eTermType.TK2dCollection : ObjType = typeof(tk2dSpriteCollection); break; +#endif + +#if TextMeshPro + case eTermType.TextMeshPFont : ObjType = typeof(TMP_FontAsset); break; +#endif + +#if SVG + case eTermType.SVGAsset : ObjType = typeof(SVGImporter.SVGAsset); break; +#endif + + case eTermType.Object : ObjType = typeof(Object); break; + } + + if (Obj!=null && !string.IsNullOrEmpty(MultiSpriteName)) + { + string sPath = AssetDatabase.GetAssetPath(Obj); + Object[] objs = AssetDatabase.LoadAllAssetRepresentationsAtPath(sPath); + Obj = null; + for (int j=0, jmax=objs.Length; j + { + term.SetTranslation(langIdx, translation, specialization); + i2source.Editor_SetDirty(); + }, specialization); + } + + static void EditPluralTranslations( ref string translation, int langIdx, string langCode ) + { + bool hasParameters = false; + int paramStart = translation.IndexOf("{["); + hasParameters = paramStart >= 0 && translation.IndexOf ("]}", paramStart) > 0; + + if (mShowPlural == langIdx && string.IsNullOrEmpty (translation)) + mShowPlural = -1; + + bool allowPlural = hasParameters || translation.Contains("[i2p_"); + + if (allowPlural) + { + if (GUILayout.Toggle (mShowPlural == langIdx, "", EditorStyles.foldout, GUILayout.Width (13))) + mShowPlural = langIdx; + else if (mShowPlural == langIdx) + mShowPlural = -1; + + GUILayout.Space (-5); + } + + string finalTranslation = ""; + bool unfolded = mShowPlural == langIdx; + bool isPlural = allowPlural && translation.Contains("[i2p_"); + if (unfolded) + GUILayout.BeginVertical ("Box"); + + ShowPluralTranslation("Plural", langCode, translation, ref finalTranslation, true, unfolded, unfolded|isPlural ); + ShowPluralTranslation("Zero", langCode, translation, ref finalTranslation, unfolded, true, true ); + ShowPluralTranslation("One", langCode, translation, ref finalTranslation, unfolded, true, true ); + ShowPluralTranslation("Two", langCode, translation, ref finalTranslation, unfolded, true, true ); + ShowPluralTranslation("Few", langCode, translation, ref finalTranslation, unfolded, true, true ); + ShowPluralTranslation("Many", langCode, translation, ref finalTranslation, unfolded, true, true ); + + if (unfolded) + GUILayout.EndVertical (); + + translation = finalTranslation; + } + + static void ShowPluralTranslation(string pluralType, string langCode, string translation, ref string finalTranslation, bool show, bool allowDelete, bool showTag ) + { + string tag = "[i2p_" + pluralType + "]"; + int idx0 = translation.IndexOf (tag, StringComparison.OrdinalIgnoreCase); + bool hasTranslation = idx0 >= 0 || pluralType=="Plural"; + if (idx0 < 0) idx0 = 0; + else idx0 += tag.Length; + + int idx1 = translation.IndexOf ("[i2p_", idx0, StringComparison.OrdinalIgnoreCase); + if (idx1 < 0) idx1 = translation.Length; + + var pluralTranslation = translation.Substring(idx0, idx1-idx0); + var newTrans = pluralTranslation; + + bool allowPluralForm = GoogleLanguages.LanguageHasPluralType (langCode, pluralType); + + if (hasTranslation && !allowPluralForm) { + newTrans = ""; + GUI.changed = true; + } + + if (show && allowPluralForm) + { + if (!hasTranslation) + GUI.color = new Color(GUI.color.r, GUI.color.g, GUI.color.b, 0.35f); + + GUILayout.BeginHorizontal (); + if (showTag) + GUILayout.Label (pluralType, EditorStyles.miniLabel, GUILayout.Width(35)); + newTrans = EditorGUILayout.TextArea (pluralTranslation, Style_WrapTextField); + + if (allowDelete && GUILayout.Button("X", EditorStyles.toolbarButton, GUILayout.Width(15))) + { + newTrans = string.Empty; + GUI.changed = true; + GUIUtility.keyboardControl = 0; + } + + GUILayout.EndHorizontal (); + if (!hasTranslation) + GUI.color = new Color(GUI.color.r, GUI.color.g, GUI.color.b, 1); + } + + if (!string.IsNullOrEmpty (newTrans)) + { + if (hasTranslation || newTrans != pluralTranslation) + { + if (pluralType != "Plural") + finalTranslation += tag; + finalTranslation += newTrans; + } + } + } + + /*static public int DrawTranslationTabs( int Index ) + { + GUIStyle MyStyle = new GUIStyle(GUI.skin.FindStyle("dragtab")); + MyStyle.fixedHeight=0; + + GUILayout.BeginHorizontal(); + for (int i=0; i mSelectedScenes = new List(); + + public enum eToolsMode { Parse, Categorize, Merge, NoLocalized, Script, CharSet } + public eToolsMode mCurrentToolsMode = eToolsMode.Parse; + + #endregion + + #region GUI + + void OnGUI_Tools( bool reset ) + { + GUILayout.Space(10); + eToolsMode OldMode = mCurrentToolsMode; + mCurrentToolsMode = (eToolsMode)GUITools.DrawShadowedTabs ((int)mCurrentToolsMode, new[]{"Parse", "Categorize", "Merge", "No Localized", "Script", "CharSet"}, 30); + if (mCurrentToolsMode != OldMode || reset) + { + ClearErrors(); + if (mCurrentToolsMode == eToolsMode.Script) + SelectTermsFromScriptLocalization(); + OnGUI_ScenesList_SelectAllScenes(true); + } + + switch (mCurrentToolsMode) + { + case eToolsMode.Parse : OnGUI_Tools_ParseTerms(); break; + case eToolsMode.Categorize : OnGUI_Tools_Categorize(); break; + case eToolsMode.Merge : OnGUI_Tools_MergeTerms(); break; + case eToolsMode.NoLocalized : OnGUI_Tools_NoLocalized(); break; + case eToolsMode.Script : OnGUI_Tools_Script(); break; + case eToolsMode.CharSet : OnGUI_Tools_CharSet(); break; + } + OnGUI_ShowMsg(); + } + + #endregion + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools.cs.meta new file mode 100644 index 00000000..f712a581 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 3fa4621ebd4134e1989b73eb3f7b864f +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_Categorize.cs b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_Categorize.cs new file mode 100644 index 00000000..df6949ba --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_Categorize.cs @@ -0,0 +1,226 @@ +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + public partial class LocalizationEditor + { + #region Variables + + Vector2 mScrollPos_CategorizedKeys = Vector2.zero; + string mNewCategory = string.Empty; + + #endregion + + #region GUI + + void OnGUI_Tools_Categorize() + { + OnGUI_ScenesList(true); + + GUI.backgroundColor = Color.Lerp (Color.gray, Color.white, 0.2f); + GUILayout.BeginVertical(LocalizeInspector.GUIStyle_OldTextArea, GUILayout.Height(1)); + GUI.backgroundColor = Color.white; + GUILayout.Space (5); + + EditorGUILayout.HelpBox("This tool changes the category of the selected Terms and updates the highlighted scenes", MessageType.Info); + + GUILayout.Space (5); + GUITools.CloseHeader(); + + OnGUI_Tools_Categorize_Terms(); + OnGUI_NewOrExistingCategory(); + } + + void OnGUI_Tools_Categorize_Terms() + { + GUILayout.Label("Change Category of the following Terms:", EditorStyles.toolbarButton, GUILayout.ExpandWidth(true)); + + GUI.backgroundColor = Color.Lerp(GUITools.LightGray, Color.white, 0.5f); + mScrollPos_CategorizedKeys = GUILayout.BeginScrollView( mScrollPos_CategorizedKeys, LocalizeInspector.GUIStyle_OldTextArea, GUILayout.Height ( 100)); + GUI.backgroundColor = Color.white; + + if (mSelectedKeys.Count==0) + { + GUILayout.FlexibleSpace(); + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + //GUILayout.BeginVertical(); + EditorGUILayout.HelpBox("No Terms has been selected", MessageType.Warning); + /*if (GUILayout.Button("Select Terms", EditorStyles.toolbarButton, GUILayout.ExpandWidth(true))) + mCurrentViewMode = eViewMode.Keys;*/ + //GUILayout.EndVertical(); + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + GUILayout.FlexibleSpace(); + } + else + { + bool DoubleColumn = mSelectedKeys.Count>5; + int HalfCount = Mathf.CeilToInt(mSelectedKeys.Count/2.0f); + + for (int i=0, imax=mSelectedKeys.Count; i=HalfCount) break; + + GUILayout.BeginHorizontal(); + OnGUI_CategorizedTerm(mSelectedKeys[i]); + + if (DoubleColumn && i+HalfCount Categories = LocalizationManager.GetCategories(); + + for (int i=0, imax=Categories.Count; i TermReplacements; + + void AssignCategoryToSelectedTerms() + { + mIsParsing = true; + + EditorApplication.update -= AssignCategoryToSelectedTerms; + + mNewCategory = mNewCategory.Trim (LanguageSourceData.CategorySeparators); + + if (mNewCategory==LanguageSourceData.EmptyCategory) + mNewCategory = string.Empty; + + TermReplacements = new Dictionary(StringComparer.Ordinal); + for (int i=mSelectedKeys.Count-1; i>=0; --i) + { + string sKey, sCategory; + string OldTerm = mSelectedKeys[i]; + + LanguageSourceData.DeserializeFullTerm( OldTerm, out sKey, out sCategory ); + if (!string.IsNullOrEmpty(mNewCategory)) + sKey = string.Concat(mNewCategory, "/", sKey); + + if (OldTerm == sKey) + continue; + + TermReplacements[ OldTerm ] = sKey; + if (!mLanguageSource.ContainsTerm(sKey)) + { + TermData termData = mLanguageSource.GetTermData( OldTerm ); + if (termData != null) + termData.Term = sKey; + else + TermReplacements.Remove (OldTerm); + mLanguageSource.Editor_SetDirty(); + } + } + if (TermReplacements.Count<=0) + { + ShowError ("Unable to assign category: Terms were not found in the selected LanguageSource"); + } + else + { + mLanguageSource.UpdateDictionary(true); + ExecuteActionOnSelectedScenes( ReplaceTermsInCurrentScene ); + ParseTerms(true, false, true); + + if (string.IsNullOrEmpty(mNewCategory)) + mNewCategory = LanguageSourceData.EmptyCategory; + if (!mSelectedCategories.Contains(mNewCategory)) + mSelectedCategories.Add (mNewCategory); + //RemoveUnusedCategoriesFromSelected(); + ScheduleUpdateTermsToShowInList(); + } + TermReplacements = null; + mIsParsing = false; + } + + public static void ReplaceTermsInCurrentScene() + { + Localize[] Locals = (Localize[])Resources.FindObjectsOfTypeAll(typeof(Localize)); + + if (Locals==null) + return; + + bool changed = false; + for (int i=0, imax=Locals.Length; i mCharSetTool_Languages = new List(); + string mCharSet = string.Empty; + bool mCharSetTool_CaseSensitive; + + #endregion + + #region GUI Generate Script + + void OnGUI_Tools_CharSet() + { + bool computeSet = false; + + // remove missing languages + for (int i=mCharSetTool_Languages.Count-1; i>=0; --i) + { + if (mLanguageSource.GetLanguageIndex(mCharSetTool_Languages[i])<0) + mCharSetTool_Languages.RemoveAt(i); + } + + GUILayout.BeginHorizontal (EditorStyles.toolbar); + GUILayout.Label ("Languages:", EditorStyles.miniLabel, GUILayout.ExpandWidth(true)); + if (GUILayout.Button ("All", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) + { + mCharSetTool_Languages.Clear (); + mCharSetTool_Languages.AddRange (mLanguageSource.mLanguages.Select(x=>x.Name)); + computeSet = true; + } + if (GUILayout.Button ("None", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) + { + mCharSetTool_Languages.Clear (); + computeSet = true; + } + if (GUILayout.Button ("Invert", EditorStyles.toolbarButton, GUILayout.ExpandWidth(false))) + { + var current = mCharSetTool_Languages.ToList (); + mCharSetTool_Languages.Clear (); + mCharSetTool_Languages.AddRange (mLanguageSource.mLanguages.Select(x=>x.Name).Where(j=>!current.Contains(j))); + computeSet = true; + } + + + GUILayout.EndHorizontal (); + + //--[ Language List ]-------------------------- + + GUI.backgroundColor = Color.Lerp(GUITools.LightGray, Color.white, 0.5f); + mScrollPos_Languages = GUILayout.BeginScrollView( mScrollPos_Languages, LocalizeInspector.GUIStyle_OldTextArea, GUILayout.MinHeight (100), GUILayout.MaxHeight(Screen.height), GUILayout.ExpandHeight(false)); + GUI.backgroundColor = Color.white; + + for (int i=0, imax=mLanguageSource.mLanguages.Count; i (); + var LanIndexes = new List (); + for (int i=0; i c).ToArray() ); + mCharSet = Encoding.UTF8.GetString(bytes); + } + + void AppendToCharSet( HashSet sb, string text, bool isRTL ) + { + if (string.IsNullOrEmpty (text)) + return; + + text = RemoveTagsPrefix(text, "[i2p_"); + text = RemoveTagsPrefix(text, "[i2s_"); + + if (isRTL) + text = RTLFixer.Fix( text ); + + foreach (char c in text) + { + if (!mCharSetTool_CaseSensitive) + { + sb.Add(char.ToLowerInvariant(c)); + sb.Add(char.ToUpperInvariant(c)); + } + else + sb.Add(c); + } + } + + // Given "[i2p_" it removes all tags that start with that (e.g. [i2p_Zero] [i2p_One], etc) + string RemoveTagsPrefix(string text, string tagPrefix) + { + int idx = 0; + while (idx < text.Length) + { + idx = text.IndexOf(tagPrefix); + if (idx < 0) + break; + + int idx2 = text.IndexOf(']', idx); + if (idx2 < 0) + break; + + text = text.Remove(idx, idx2 - idx+1); + } + return text; + + } + + + + #endregion + } +} diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_CharSet.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_CharSet.cs.meta new file mode 100644 index 00000000..e54fcfa8 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_CharSet.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 104713279df05ac4b96f5a76ab621c8a +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_MergeTerms.cs b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_MergeTerms.cs new file mode 100644 index 00000000..05d4f857 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_MergeTerms.cs @@ -0,0 +1,157 @@ +using System; +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + public partial class LocalizationEditor + { + #region Variables + + #endregion + + #region GUI + + void OnGUI_Tools_MergeTerms() + { + OnGUI_ScenesList(true); + + GUI.backgroundColor = Color.Lerp (Color.gray, Color.white, 0.2f); + GUILayout.BeginVertical(LocalizeInspector.GUIStyle_OldTextArea, GUILayout.Height(1)); + GUI.backgroundColor = Color.white; + GUILayout.Space (5); + + EditorGUILayout.HelpBox("This option replace all occurrences of this key in the selected scenes", MessageType.Info); + + GUILayout.Space (5); + GUITools.CloseHeader(); + + OnGUI_Tools_Categorize_Terms(); + OnGUI_NewOrExistingTerm(); + } + + void OnGUI_NewOrExistingTerm() + { + if (mKeyToExplore==null) + mKeyToExplore = string.Empty; + + GUI.backgroundColor = Color.Lerp (Color.gray, Color.white, 0.2f); + GUILayout.BeginVertical(LocalizeInspector.GUIStyle_OldTextArea, GUILayout.Height(1)); + GUI.backgroundColor = Color.white; + GUILayout.Space(5); + GUILayout.Label("Replace By:"); + GUILayout.EndVertical(); + + //--[ Create Term ]------------------------ + GUILayout.BeginHorizontal(); + mKeyToExplore = GUILayout.TextField(mKeyToExplore, EditorStyles.toolbarTextField, GUILayout.ExpandWidth(true)); + if (GUILayout.Button("Create", "toolbarbutton", GUILayout.Width(60))) + { + LanguageSourceData.ValidateFullTerm( ref mKeyToExplore ); + EditorApplication.update += ReplaceSelectedTerms; + } + GUILayout.EndHorizontal(); + + //--[ Existing Term ]------------------------ + int Index = 0; + List Terms = mLanguageSource.GetTermsList(); + + for (int i=0, imax=Terms.Count; i(StringComparer.Ordinal); + for (int i=mSelectedKeys.Count-1; i>=0; --i) + { + string OldTerm = mSelectedKeys[i]; + if (OldTerm == sNewKey) + continue; + + TermReplacements[ OldTerm ] = mKeyToExplore; + DeleteTerm(OldTerm); + } + ExecuteActionOnSelectedScenes( ReplaceTermsInCurrentScene ); + DoParseTermsInCurrentScene(); + + //--[ Update Selected Categories ]------------- + string mNewCategory = LanguageSourceData.GetCategoryFromFullTerm(sNewKey); + if (mNewCategory == string.Empty) + mNewCategory = LanguageSourceData.EmptyCategory; + if (!mSelectedCategories.Contains(mNewCategory)) + mSelectedCategories.Add (mNewCategory); + //RemoveUnusedCategoriesFromSelected(); + ScheduleUpdateTermsToShowInList(); + TermReplacements = null; + mIsParsing = false; + } + + void RemoveUnusedCategoriesFromSelected() + { + List Categories = LocalizationManager.GetCategories(); + for (int i=mSelectedCategories.Count-1; i>=0; --i) + if (!Categories.Contains( mSelectedCategories[i] )) + mSelectedCategories.RemoveAt(i); + + if (mSelectedCategories.Count == 0) + mSelectedCategories.AddRange(Categories); + + ScheduleUpdateTermsToShowInList(); + } + + #endregion + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_MergeTerms.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_MergeTerms.cs.meta new file mode 100644 index 00000000..52785835 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_MergeTerms.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: d59fa53e0df75a34693c4673bba5ed94 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_NoLocalized.cs b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_NoLocalized.cs new file mode 100644 index 00000000..e4a11e53 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_NoLocalized.cs @@ -0,0 +1,163 @@ +using System.Collections.Generic; +using TMPro; +using UnityEditor; +using UnityEngine; +using UnityEngine.UI; + +namespace TEngine.Localization +{ + public partial class LocalizationEditor + { + #region Variables + static string _Tools_NoLocalized_Include, + _Tools_NoLocalized_Exclude; + const string _Help_Tool_NoLocalized = "This selects all labels in the current scene that don't have a Localized component.\n\nWhen Include or Exclude are set, labels will be filtered based on those settings.Separate by (,) if multiple strings are used.\n(e.g. Include:\"example,tutorial\")"; + #endregion + + #region GUI Find NoLocalized Terms + + void OnGUI_Tools_NoLocalized() + { + //OnGUI_ScenesList(); + + if (_Tools_NoLocalized_Include==null) + { + _Tools_NoLocalized_Include = EditorPrefs.GetString ("_Tools_NoLocalized_Include", string.Empty); + _Tools_NoLocalized_Exclude = EditorPrefs.GetString ("_Tools_NoLocalized_Exclude", string.Empty); + } + + GUILayout.Space (5); + + GUI.backgroundColor = Color.Lerp (Color.gray, Color.white, 0.2f); + GUILayout.BeginVertical(LocalizeInspector.GUIStyle_OldTextArea, GUILayout.Height(1)); + GUI.backgroundColor = Color.white; + + EditorGUILayout.HelpBox(_Help_Tool_NoLocalized, MessageType.Info); + + GUILayout.Space(5); + GUILayout.BeginHorizontal(); + GUILayout.Label ("Include:", GUILayout.Width(60)); + _Tools_NoLocalized_Include = EditorGUILayout.TextArea(_Tools_NoLocalized_Include, GUILayout.ExpandWidth(true)); + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal(); + GUILayout.Label ("Exclude:", GUILayout.Width(60)); + _Tools_NoLocalized_Exclude = EditorGUILayout.TextArea(_Tools_NoLocalized_Exclude, GUILayout.ExpandWidth(true)); + GUILayout.EndHorizontal(); + + GUILayout.Space (5); + + GUILayout.BeginHorizontal (); + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Select No Localized Labels")) + EditorApplication.update += SelectNoLocalizedLabels; + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + GUILayout.EndVertical(); + } + + #endregion + + + #region Find No Localized + + void SelectNoLocalizedLabels() + { + EditorPrefs.SetString ("_Tools_NoLocalized_Include", _Tools_NoLocalized_Include); + EditorPrefs.SetString ("_Tools_NoLocalized_Exclude", _Tools_NoLocalized_Exclude); + + EditorApplication.update -= SelectNoLocalizedLabels; + + List labels = new List(); + + TextMesh[] textMeshes = (TextMesh[])Resources.FindObjectsOfTypeAll(typeof(TextMesh)); + if (textMeshes!=null && textMeshes.Length>0) + labels.AddRange(textMeshes); + +#if NGUI + UILabel[] uiLabels = (UILabel[])Resources.FindObjectsOfTypeAll(typeof(UILabel)); + if (uiLabels!=null && uiLabels.Length>0) + labels.AddRange(uiLabels); +#endif + Text[] uiTexts = (Text[])Resources.FindObjectsOfTypeAll(typeof(Text)); + if (uiTexts!=null && uiTexts.Length>0) + labels.AddRange(uiTexts); +#if TextMeshPro + TextMeshPro[] tmpText = (TextMeshPro[])Resources.FindObjectsOfTypeAll(typeof(TextMeshPro)); + if (tmpText!=null && tmpText.Length>0) + labels.AddRange(tmpText); + + TextMeshProUGUI[] uiTextsUGUI = (TextMeshProUGUI[])Resources.FindObjectsOfTypeAll(typeof(TextMeshProUGUI)); + if (uiTextsUGUI!=null && uiTextsUGUI.Length>0) + labels.AddRange(uiTextsUGUI); +#endif +#if TK2D + tk2dTextMesh[] tk2dTM = (tk2dTextMesh[])Resources.FindObjectsOfTypeAll(typeof(tk2dTextMesh)); + if (tk2dTM!=null && tk2dTM.Length>0) + labels.AddRange(tk2dTM); +#endif + + if (labels.Count==0) + return; + + string[] Includes = null; + string[] Excludes = null; + + if (!string.IsNullOrEmpty (_Tools_NoLocalized_Include)) + Includes = _Tools_NoLocalized_Include.ToLower().Split(',', ';'); + + if (!string.IsNullOrEmpty (_Tools_NoLocalized_Exclude)) + Excludes = _Tools_NoLocalized_Exclude.ToLower().Split(',', ';'); + + List Objs = new List(); + + for (int i=0, imax=labels.Count; i()!=null) + continue; + + if (ShouldFilter(label.name.ToLower(), Includes, Excludes)) + continue; + + Objs.Add( labels[i].gameObject ); + } + + if (Objs.Count>0) + Selection.objects = Objs.ToArray(); + else + ShowWarning("All labels in this scene have a Localize component assigned"); + } + + bool ShouldFilter( string Text, string[] Includes, string[] Excludes ) + { + if (Includes!=null && Includes.Length>0) + { + bool hasAny = false; + for (int j=0; j0) + { + for (int j=0; j mParsedTerms = new SortedDictionary(StringComparer.Ordinal); // All Terms resulted from parsing the scenes and collecting the Localize.Term and how many times the terms are used + public static HashSet mParsedCategories = new HashSet(StringComparer.Ordinal); + + public static List mShowableTerms = new List (); // this contains the terms from mParsedTerms that should be shown in the list (filtered by search string, usage, etc) + public static bool mParseTermsIn_Scenes = true; + public static bool mParseTermsIn_Scripts = true; + + #endregion + + #region GUI Parse Keys + + void OnGUI_Tools_ParseTerms() + { + OnGUI_ScenesList(); + + GUI.backgroundColor = Color.Lerp (Color.gray, Color.white, 0.2f); + GUILayout.BeginVertical(LocalizeInspector.GUIStyle_OldTextArea, GUILayout.Height(1)); + GUI.backgroundColor = Color.white; + + GUILayout.Space (5); + + EditorGUILayout.HelpBox("This tool searches all Terms used in the selected scenes and updates the usage counter in the Terms Tab", MessageType.Info); + + GUILayout.Space (5); + + GUILayout.BeginHorizontal (); + GUILayout.FlexibleSpace(); + GUILayout.BeginHorizontal ("Box"); + mParseTermsIn_Scenes = GUILayout.Toggle(mParseTermsIn_Scenes, new GUIContent("Parse SCENES", "Opens the selected scenes and finds all the used terms")); + GUILayout.FlexibleSpace(); + mParseTermsIn_Scripts = GUILayout.Toggle(mParseTermsIn_Scripts, new GUIContent("Parse SCRIPTS", "Searches all .cs files and counts all terms like: ScriptLocalization.Get(\"xxx\")")); + GUILayout.EndHorizontal(); + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + GUILayout.BeginHorizontal (); + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Parse Localized Terms")) + { + EditorApplication.update += ParseTermsInSelectedScenes; + if (mParseTermsIn_Scripts) + EditorApplication.update += ParseTermsInScripts; + } + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + GUILayout.EndVertical(); + } + + #endregion + + #region Parsed Terms Handlers + + public static ParsedTerm GetParsedTerm( string Term ) + { + ParsedTerm data; + if (!mParsedTerms.TryGetValue(Term, out data)) + data = AddParsedTerm(Term, null, null, 0); + return data; + } + + static ParsedTerm AddParsedTerm( string FullTerm, string TermKey, string Category, int Usage ) + { + if (TermKey==null) + LanguageSourceData.DeserializeFullTerm(FullTerm, out TermKey, out Category); + + var data = new ParsedTerm(); + data.Usage = Usage; + data.FullTerm = FullTerm; + data.Term = TermKey; + data.Category = Category; + + mParsedTerms[FullTerm] = data; + return data; + } + + public static void RemoveParsedTerm( string Term ) + { + mParsedTerms.Remove(Term); + } + + public static void DecreaseParsedTerm( string Term ) + { + ParsedTerm data = GetParsedTerm(Term); + data.Usage = Mathf.Max (0, data.Usage-1); + } + + static void UpdateParsedCategories() + { + mParsedCategories.Clear(); + mParsedCategories.UnionWith( mParsedTerms.Select(x=>x.Value.Category) ); + + mSelectedCategories.RemoveAll(x=>!mParsedCategories.Contains(x)); + } + + + #endregion + + #region ParseKeys + + public static void ParseTermsInSelectedScenes() + { + EditorApplication.update -= ParseTermsInSelectedScenes; + ParseTerms(false, false, true); + } + + public static void DoParseTermsInCurrentScene() + { + EditorApplication.update -= DoParseTermsInCurrentScene; + ParseTerms(true, false, true); + } + + public static void DoParseTermsInCurrentSceneAndScripts() + { + EditorApplication.update -= DoParseTermsInCurrentSceneAndScripts; + ParseTerms(true, true, true); + } + + static void ParseTerms(bool OnlyCurrentScene, bool ParseScripts, bool OpenTermsTab) + { + mIsParsing = true; + + mParsedTerms.Clear(); + mSelectedKeys.Clear(); + mParsedCategories.Clear(); + + if (ParseScripts) + { + ParseTermsInScripts(); + FindTermsInLocalizedStrings(); + } + + if (mParseTermsIn_Scenes) + { + if (!OnlyCurrentScene) + ExecuteActionOnSelectedScenes(FindTermsInCurrentScene); + else + FindTermsInCurrentScene(); + } + + FindTermsNotUsed(); + ScheduleUpdateTermsToShowInList(); + + + if (mParsedTerms.Count <= 0) + { + ShowInfo("No terms where found during parsing"); + return; + } + + UpdateParsedCategories(); + { + mSelectedCategories.Clear(); + mSelectedCategories.AddRange(mParsedCategories); + } + + + if (mLanguageSource!=null) + { + var sourceCategories = mLanguageSource.GetCategories(); + mSelectedCategories.RemoveAll(x => !sourceCategories.Contains(x)); + } + + if (OpenTermsTab) + { + if ((mFlagsViewKeys & (int)eFlagsViewKeys.Missing) > 0) + { + mFlagsViewKeys = (int)eFlagsViewKeys.Used | (int)eFlagsViewKeys.NotUsed | (int)eFlagsViewKeys.Missing; + } + else + { + mFlagsViewKeys = (int)eFlagsViewKeys.Used | (int)eFlagsViewKeys.NotUsed; + } + mCurrentViewMode = eViewMode.Keys; + } + mIsParsing = false; + } + + static void FindTermsInCurrentScene() + { + Localize[] Locals = (Localize[])Resources.FindObjectsOfTypeAll(typeof(Localize)); + + if (Locals==null) + return; + + for (int i=0, imax=Locals.Length; i Type_localizedString.IsAssignableFrom(x.PropertyType) || + Attribute.IsDefined(x, typeof(TermsPopup))); + foreach (var p in props) + { + string value = null; + if (Type_localizedString.IsAssignableFrom(p.PropertyType)) + { + var varObj = p.GetValue(cmp,null); + value = Convert.ToString(varObj.GetType().GetField("mTerm").GetValue(varObj)); + } + else + { + value = Convert.ToString(p.GetValue(cmp,null)); + } + if (!string.IsNullOrEmpty(value)) + { + GetParsedTerm(value).Usage++; + } + + //Debug.LogFormat("{0} ({1})", p.Name, p.PropertyType); + //Debug.Log(value); + } + + + var variables = cmp.GetType() + .GetFields() + .Where(x => Type_localizedString.IsAssignableFrom(x.FieldType) || + Attribute.IsDefined(x, typeof(TermsPopup))); + foreach (var v in variables) + { + string value = null; + if (Type_localizedString.IsAssignableFrom(v.FieldType)) + { + var varObj = v.GetValue(cmp); + value = Convert.ToString(varObj.GetType().GetField("mTerm").GetValue(varObj)); + } + else + { + value = Convert.ToString(v.GetValue(cmp)); + } + if (!string.IsNullOrEmpty(value)) + { + GetParsedTerm(value).Usage++; + } + //Debug.LogFormat("{0} ({1})", v.Name, v.FieldType); + //Debug.Log(value); + } + } + } + + static void FindTermsNotUsed() + { + // every Term that is in the DB but not in mParsedTerms + if (mLanguageSource == null) + return; + + //string lastCategory = null; + foreach (TermData termData in mLanguageSource.mTerms) + GetParsedTerm(termData.Term); + } + + static void ParseTermsInScripts() + { + EditorApplication.update -= ParseTermsInScripts; + + string[] scriptFiles = AssetDatabase.GetAllAssetPaths().Where(path => path.ToLower().EndsWith(".cs")).ToArray(); + + string mLocalizationManager = @"GetTranslation\s?\(\s?\""(.*?)\"""; + string mLocalizationManagerOld = @"GetTermTranslation\s?\(\s?\""(.*?)\"""; + string mLocalizationManagerTry = @"TryGetTranslation\s?\(\s?\""(.*?)\"""; + string mSetTerm = @"SetTerm\s?\(\s?\""(.*?)\"""; + + Regex regex = new Regex(mLocalizationManager + "|" + mLocalizationManagerTry + "|" + mLocalizationManagerOld + "|" + mSetTerm, RegexOptions.Multiline); + + foreach (string scriptFile in scriptFiles) + { + string scriptContents = File.ReadAllText(scriptFile); + MatchCollection matches = regex.Matches(scriptContents); + for (int matchNum = 0; matchNum < matches.Count; matchNum++) + { + Match match = matches[matchNum]; + string term = I2Utils.GetCaptureMatch(match); + GetParsedTerm(term).Usage++; + } + } + ScheduleUpdateTermsToShowInList(); + } + #endregion + + #region Misc + + public static void SetAllTerms_When_InferredTerms_IsInSource() + { + var Locals = Resources.FindObjectsOfTypeAll(typeof(Localize)) as Localize[]; + + if (Locals==null) + return; + + foreach (var localize in Locals) + { + if (localize == null || localize.Source != null && localize.Source.SourceData != mLanguageSource || localize.gameObject == null || !GUITools.ObjectExistInScene (localize.gameObject)) + continue; + + if (!string.IsNullOrEmpty (localize.mTerm) && !string.IsNullOrEmpty (localize.SecondaryTerm)) + continue; + + ApplyInferredTerm( localize ); + } + + ParseTerms (true, false, true); + } + + public static void ApplyInferredTerm( Localize localize) + { + if (mLanguageSource==null) + return; + if (!string.IsNullOrEmpty (localize.mTerm) && !string.IsNullOrEmpty (localize.mTermSecondary)) + return; + + string sTerm, sSecTerm; + localize.GetFinalTerms (out sTerm, out sSecTerm); + + if (string.IsNullOrEmpty (localize.mTerm)) + { + var termData = mLanguageSource.GetTermData (sTerm, true); + if (termData!=null) + localize.mTerm = termData.Term; + } + + if (string.IsNullOrEmpty (localize.mTermSecondary)) + { + var termData = mLanguageSource.GetTermData (sSecTerm, true); + if (termData!=null) + localize.mTermSecondary = termData.Term; + } + + //localize.Source = mLanguageSource; + } + + #endregion + } +} diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_ParseTerms.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_ParseTerms.cs.meta new file mode 100644 index 00000000..093359fe --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_ParseTerms.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 697e1bc5d373845df927c0da625b7cad +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_Scenes.cs b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_Scenes.cs new file mode 100644 index 00000000..94a70a08 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Tools_Scenes.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + public partial class LocalizationEditor + { + #region Variables + EditorBuildSettingsScene[] mScenesInBuildSettings; + bool Tools_ShowScenesList; + #endregion + + #region GUI + + void OnGUI_ScenesList( bool SmallSize = false ) + { + mScenesInBuildSettings = EditorBuildSettings.scenes; + string currentScene = Editor_GetCurrentScene (); + + List sceneList = mScenesInBuildSettings.Select(x=>x.path).ToList(); + if (!sceneList.Contains (currentScene)) + sceneList.Insert (0, currentScene); + + mSelectedScenes.RemoveAll (x => !sceneList.Contains(x)); + if (mSelectedScenes.Count==0) + mSelectedScenes.Add (currentScene); + + if (!Tools_ShowScenesList) + { + GUILayout.Space(5); + GUILayout.BeginHorizontal(); + Tools_ShowScenesList = GUILayout.Toggle(Tools_ShowScenesList, "", EditorStyles.foldout, GUILayout.ExpandWidth(false)); + + string sceneText = string.Empty; + if (mSelectedScenes.Count==1 && mSelectedScenes[0]== currentScene) + sceneText = "Current Scene"; + else + sceneText = string.Format("{0} of {1} Scenes", mSelectedScenes.Count, Mathf.Max(mScenesInBuildSettings.Length, mSelectedScenes.Count)); + var stl = new GUIStyle("toolbarbutton"); + stl.richText = true; + if (GUILayout.Button("Scenes to Parse: "+sceneText+"", stl)) + Tools_ShowScenesList = true; + GUILayout.EndHorizontal(); + GUILayout.Space(10); + return; + } + OnGUI_ScenesList_TitleBar(); + + GUI.backgroundColor = Color.Lerp(GUITools.LightGray, Color.white, 0.5f); + mScrollPos_BuildScenes = GUILayout.BeginScrollView( mScrollPos_BuildScenes, LocalizeInspector.GUIStyle_OldTextArea, GUILayout.Height ( SmallSize ? 100 : 200)); + GUI.backgroundColor = Color.white; + + for (int i=0, imax=sceneList.Count; i0) + { + try + { + string FilePath = AssetDatabase.GUIDToAssetPath(assets[0]); + return FilePath; + } + catch(Exception) + { } + } + + return "Assets/ScriptLocalization.cs"; + } + + void BuildScriptWithSelectedTerms( StringBuilder sbTrans, StringBuilder sbTerms ) + { + List Categories = LocalizationManager.GetCategories(); + foreach (string Category in Categories) + { + List CategoryTerms = ScriptTool_GetSelectedTermsInCategory(Category); + if (CategoryTerms.Count<=0) + continue; + + List AdjustedCategoryTerms = new List(CategoryTerms); + for (int i=0, imax=AdjustedCategoryTerms.Count; i ScriptTool_GetSelectedTermsInCategory( string Category ) + { + List list = new List(); + foreach (string FullKey in mSelectedKeys) + { + string categ = LanguageSourceData.GetCategoryFromFullTerm(FullKey); + if (categ == Category && ShouldShowTerm(FullKey)) + { + list.Add( LanguageSourceData.GetKeyFromFullTerm(FullKey) ); + } + } + + return list; + } + + void BuildScriptCategory( StringBuilder sbTrans, StringBuilder sbTerms, string Category, List AdjustedTerms, List Terms ) + { + if (Category==LanguageSourceData.EmptyCategory) + { + for (int i = 0; i < Terms.Count; ++i) + { + sbTrans.AppendLine( " public static string " + AdjustedTerms[i] + " \t\t{ get{ return LocalizationManager.GetTranslation (\"" + Terms[i] + "\"); } }"); + sbTerms.AppendLine(" public const string " + AdjustedTerms[i] + " = \"" + Terms[i] + "\";"); + } + } + else + for (int i=0; i=0) + Term = "_"+Term; + + if (!allowFullLength && Term.Length>Script_Tool_MaxVariableLength) + Term = Term.Substring(0, Script_Tool_MaxVariableLength); + + // Remove invalid characters + char[] chars = Term.ToCharArray(); + for (int i=0, imax=chars.Length; i=0) return true; + return c>='\u4e00' && c<='\u9fff'; // Chinese/Japanese characters + } + } + + void ScriptTool_EnumerateDuplicatedTerms(List AdjustedTerms) + { + string lastTerm = "$"; + int Counter = 1; + for (int i=0, imax=AdjustedTerms.Count; i +// { +// EditorApplication.update -= Callback; + +// if (source.GetComponents().Length<=2) +// { +// Debug.Log ("Deleting GameObject '" + source.name + "' and Openning the "+LocalizationManager.GlobalSources[0]+".prefab"); +// DestroyImmediate (source.gameObject); +// } +// else +// { +// Debug.Log ("Deleting the LanguageSource inside GameObject " + source.name + " and Openning the "+LocalizationManager.GlobalSources[0] +".prefab"); +// DestroyImmediate (source); +// } + +// GameObject Prefab = (Resources.Load(LocalizationManager.GlobalSources[0]) as GameObject); +// Selection.activeGameObject = Prefab; +// }; +// } +// GUILayout.FlexibleSpace(); +// GUILayout.EndHorizontal(); + +// GUILayout.Space(10); + } + + private bool bSourceInsidePluginsFolder = true; + public void OnGUI_Warning_SourceInsidePluginsFolder() + { + if (!bSourceInsidePluginsFolder || mLanguageSource.UserAgreesToHaveItInsideThePluginsFolder) + return; + + if (!mLanguageSource.IsGlobalSource()) + { + bSourceInsidePluginsFolder = false; + return; + } + + string pluginPath = UpgradeManager.GetI2LocalizationPath(); + string assetPath = AssetDatabase.GetAssetPath(target); + + if (!assetPath.StartsWith(pluginPath, StringComparison.OrdinalIgnoreCase)) + { + bSourceInsidePluginsFolder = false; + return; + } + + string Text = @"Its advised to move this Global Source to a folder outside the I2 Localization. +For example (Assets/I2/Resources) instead of (Assets/I2/Localization/Resources) + +That way upgrading the plugin its as easy as deleting the I2/Localization and I2/Common folders and reinstalling. + +Do you want the plugin to automatically move the LanguageSource to a folder outside the plugin?"; + EditorGUILayout.HelpBox(Text, MessageType.Warning); + + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Keep as is")) + { + SerializedProperty Agree = serializedObject.FindProperty("UserAgreesToHaveItInsideThePluginsFolder"); + Agree.boolValue = true; + bSourceInsidePluginsFolder = true; + } + + GUILayout.FlexibleSpace(); + + if (GUILayout.Button("Ask me later")) + { + bSourceInsidePluginsFolder = false; + } + + GUILayout.FlexibleSpace(); + if (GUILayout.Button("Move to the Recommended Folder")) + EditorApplication.delayCall += MoveGlobalSource; + + GUILayout.FlexibleSpace(); + GUILayout.EndHorizontal(); + + GUILayout.Space(10); + } + + public bool OnGUI_Warning_SourceNotUpToDate() + { + if (mProp_GoogleLiveSyncIsUptoDate.boolValue) + { + return false; + } + + string Text = "Spreadsheet is not up-to-date and Google Live Synchronization is enabled\n\nWhen playing in the device the Spreadsheet will be downloaded and override the translations built from the editor.\n\nTo fix this, Import or Export REPLACE to Google"; + EditorGUILayout.HelpBox(Text, MessageType.Warning); + return true; + } + + private static void MoveGlobalSource() + { + EditorApplication.delayCall -= MoveGlobalSource; + + string pluginPath = UpgradeManager.GetI2LocalizationPath(); + string assetPath = AssetDatabase.GetAssetPath(mLanguageSource.ownerObject); + + string I2Path = pluginPath.Substring(0, pluginPath.Length-"/Localization".Length); + string newPath = I2Path + "/Resources/" + mLanguageSource.ownerObject.name + ".prefab"; + + string fullresFolder = Application.dataPath + I2Path.Replace("Assets","") + "/Resources"; + bool folderExists = Directory.Exists (fullresFolder); + + if (!folderExists) + AssetDatabase.CreateFolder(I2Path, "Resources"); + AssetDatabase.MoveAsset(assetPath, newPath); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + + var prefab = AssetDatabase.LoadAssetAtPath(newPath, typeof(GameObject)) as GameObject; + Selection.activeGameObject = prefab; + + Debug.Log("LanguageSource moved to:" + newPath); + ShowInfo("Please, ignore some console warnings/errors produced by this operation, everything worked fine. In a new release those warnings will be cleared"); + } + + public static void DelayedDestroySource() + { + + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Warnings.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Warnings.cs.meta new file mode 100644 index 00000000..bb324deb --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Localization/LocalizationEditor_Warnings.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 20e86d3a806624846bccd81bac9f935f +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_ANDROID.cs b/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_ANDROID.cs new file mode 100644 index 00000000..e82e75a9 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_ANDROID.cs @@ -0,0 +1,151 @@ +#if UNITY_ANDROID +using UnityEditor.Callbacks; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEditor.Android; +using UnityEngine; + +namespace TEngine.Localization +{ + public class PostProcessBuild_Android + #if UNITY_2021_1_OR_NEWER + : IPostGenerateGradleAndroidProject + #endif + { + #if UNITY_2021_1_OR_NEWER + + public int callbackOrder => 0; + public void OnPostGenerateGradleAndroidProject(string path) + { + path = Path.Combine(path, "src/main"); + PostProcessAndroid(BuildTarget.Android, path); + } + + #else + + // Post Process Scene is a hack, because using PostProcessBuild will be called after the APK is generated, and so, I didn't find a way to copy the new files + [PostProcessScene] + public static void OnPostProcessScene() + { + // #if UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 + // bool isFirstScene = (EditorBuildSettings.scenes.Length>0 && EditorBuildSettings.scenes[0].path == EditorApplication.currentScene); + // #else + // bool isFirstScene = UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex <= 0; + // #endif + // + // if (!EditorApplication.isPlayingOrWillChangePlaymode && + // (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android) && + // isFirstScene) + // { + // string projPath = System.IO.Path.GetFullPath(Application.streamingAssetsPath + "/../../Temp/StagingArea"); + // //string projPath = System.IO.Path.GetFullPath(Application.dataPath+ "/Plugins/Android"); + // PostProcessAndroid(BuildTarget.Android, projPath); + // } + } + #endif + + + //[PostProcessBuild(10000)] + public static void PostProcessAndroid(BuildTarget buildTarget, string pathToBuiltProject) + { + if (buildTarget!=BuildTarget.Android) + return; + + if (LocalizationManager.Sources.Count <= 0) + LocalizationManager.UpdateSources(); + + // Get language with variants, but also add it without the variant to allow fallbacks (e.g. en-CA also adds en) + var langCodes = LocalizationManager.GetAllLanguagesCode(false).Concat( LocalizationManager.GetAllLanguagesCode(true) ).Distinct().ToList(); + + if (langCodes.Count <= 0) + return; + string stringXML = "\n"+ + "\n"+ + " {0}\n"+ + ""; + + SetStringsFile( pathToBuiltProject+"/res/values", "strings.xml", stringXML, LocalizationManager.GetAppName(langCodes[0]) ); + + + var list = new List(); + list.Add( pathToBuiltProject + "/res/values" ); + foreach (var code in langCodes) + { + // Android doesn't use zh-CN or zh-TW, instead it uses: zh-rCN, zh-rTW, zh + string fixedCode = code; + if (fixedCode.StartsWith("zh", System.StringComparison.OrdinalIgnoreCase)) + { + string googleCode = GoogleLanguages.GetGoogleLanguageCode(fixedCode); + if (googleCode==null) googleCode = fixedCode; + fixedCode = (googleCode == "zh-CN") ? "zh-CN" : googleCode; + } + fixedCode = fixedCode.Replace("-", "-r"); + + string dir = pathToBuiltProject + "/res/values-" + fixedCode; + + SetStringsFile( dir, "strings.xml", stringXML, LocalizationManager.GetAppName(code) ); + } + } + + static void CreateFileIfNeeded ( string folder, string fileName, string text ) + { + try + { + if (!System.IO.Directory.Exists( folder )) + System.IO.Directory.CreateDirectory( folder ); + + if (!System.IO.File.Exists( folder + "/"+fileName )) + System.IO.File.WriteAllText( folder + "/"+fileName, text ); + } + catch (System.Exception e) + { + Debug.Log( e ); + } + } + + static void SetStringsFile(string folder, string fileName, string stringXML, string appName) + { + try + { + appName = appName.Replace("&", "&").Replace("<", "<").Replace(">", ">").Replace("\"", "\\\"").Replace("'", "\\'"); + appName = appName.Replace("\r\n", string.Empty).Replace("\n", string.Empty).Replace("\r", string.Empty); + + if (!System.IO.Directory.Exists(folder)) + System.IO.Directory.CreateDirectory(folder); + + if (!System.IO.File.Exists(folder + "/" + fileName)) + { + // create the string file if it doesn't exist + stringXML = string.Format(stringXML, appName); + } + else + { + stringXML = System.IO.File.ReadAllText(folder + "/" + fileName); + // find app_name + var pattern = "\"app_name\">(.*)<\\/string>"; + var regexPattern = new System.Text.RegularExpressions.Regex(pattern); + if (regexPattern.IsMatch(stringXML)) + { + // Override the AppName if it was found + stringXML = regexPattern.Replace(stringXML, string.Format("\"app_name\">{0}", appName)); + } + else + { + // insert the appName if it wasn't there + int idx = stringXML.IndexOf(""); + if (idx > 0) + stringXML = stringXML.Insert(idx + "".Length, string.Format("\n {0}\n", appName)); + } + } + System.IO.File.WriteAllText(folder + "/" + fileName, stringXML); + } + catch (System.Exception e) + { + Debug.Log(e); + } + } + } +} +#endif \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_ANDROID.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_ANDROID.cs.meta new file mode 100644 index 00000000..c76dae8d --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_ANDROID.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 4c8d936531867014da228239d095833c +timeCreated: 1489354551 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_IOS.cs b/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_IOS.cs new file mode 100644 index 00000000..3b9e3150 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_IOS.cs @@ -0,0 +1,102 @@ +#if UNITY_IOS || UNITY_IPHONE +using UnityEditor.Callbacks; +using System.Collections; +using UnityEditor.iOS_I2Loc.Xcode; +using System.IO; +using UnityEditor; +using UnityEngine; +using System.Linq; + + +namespace TEngine.Localization +{ + public class PostProcessBuild_IOS + { + [PostProcessBuild(10000)] + public static void ChangeXcodePlist(BuildTarget buildTarget, string pathToBuiltProject) + { + if (buildTarget != BuildTarget.iOS) + return; + + if (LocalizationManager.Sources.Count <= 0) + LocalizationManager.UpdateSources(); + var langCodes = LocalizationManager.GetAllLanguagesCode(false).Concat(LocalizationManager.GetAllLanguagesCode(true)).Distinct().ToList(); + if (langCodes.Count <= 0) + return; + + try + { + //----[ Export localized languages to the info.plist ]--------- + + string plistPath = pathToBuiltProject + "/Info.plist"; + PlistDocument plist = new PlistDocument(); + plist.ReadFromString(File.ReadAllText(plistPath)); + + PlistElementDict rootDict = plist.root; + + // Get Language root + var langArray = rootDict.CreateArray("CFBundleLocalizations"); + + // Set the Language Codes + foreach (var code in langCodes) + { + if (code == null || code.Length < 2) + continue; + langArray.AddString(code); + } + + rootDict.SetString("CFBundleDevelopmentRegion", langCodes[0]); + + // Write to file + File.WriteAllText(plistPath, plist.WriteToString()); + + //--[ Localize App Name ]---------- + + string LocalizationRoot = pathToBuiltProject + "/I2Localization"; + if (!Directory.Exists(LocalizationRoot)) + Directory.CreateDirectory(LocalizationRoot); + + var project = new PBXProject(); + string projPath = PBXProject.GetPBXProjectPath(pathToBuiltProject); + //if (!projPath.EndsWith("xcodeproj")) + //projPath = projPath.Substring(0, projPath.LastIndexOfAny("/\\".ToCharArray())); + + project.ReadFromFile(projPath); + //var targetName = PBXProject.GetUnityTargetName(); + //string projBuild = project.TargetGuidByName( targetName ); + + project.RemoveLocalizationVariantGroup("I2 Localization"); + // Set the Language Overrides + foreach (var code in langCodes) + { + if (code == null || code.Length < 2) + continue; + + var LanguageDirRoot = LocalizationRoot + "/" + code + ".lproj"; + if (!Directory.Exists(LanguageDirRoot)) + Directory.CreateDirectory(LanguageDirRoot); + + var infoPlistPath = LanguageDirRoot + "/InfoPlist.strings"; + var InfoPlist = string.Format("CFBundleDisplayName = \"{0}\";", LocalizationManager.GetAppName(code)); + File.WriteAllText(infoPlistPath, InfoPlist); + + var langProjectRoot = "I2Localization/"+code+".lproj"; + + var stringPaths = LanguageDirRoot + "/Localizable.strings"; + File.WriteAllText(stringPaths, string.Empty); + + project.AddLocalization(langProjectRoot + "/Localizable.strings", langProjectRoot + "/Localizable.strings", "I2 Localization"); + project.AddLocalization(langProjectRoot + "/InfoPlist.strings", langProjectRoot + "/InfoPlist.strings", "I2 Localization"); + } + + project.WriteToFile(projPath); + + } + catch (System.Exception e) + { + Debug.Log (e); + } + } + } +} +#endif \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_IOS.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_IOS.cs.meta new file mode 100644 index 00000000..327da586 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_IOS.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 2bf71e767aaa5a245b9d74f326a39549 +timeCreated: 1489354551 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_UnloadLanguages.cs b/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_UnloadLanguages.cs new file mode 100644 index 00000000..d6367ceb --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_UnloadLanguages.cs @@ -0,0 +1,30 @@ +namespace TEngine.Localization +{ + public class PostProcessBuild_UnloadLanguages + { + // [PostProcessBuild] + // public static void SaveGlobalSources(BuildTarget buildTarget, string pathToBuiltProject) + // { + //if (LocalizationManager.Sources.Count <= 0) + // LocalizationManager.UpdateSources(); + + // foreach (var source in LocalizationManager.Sources.Where(x=>x.IsGlobalSource())) + // { + // source.SaveLanguages(true, PersistentStorage.eFileType.Streaming); + // } + // } + + // [PostProcessScene] + // public static void SaveLocalSources() + // { + // if (EditorApplication.isPlayingOrWillChangePlaymode) + // return; + + // LanguageSource[] sceneSources = (LanguageSource[])Resources.FindObjectsOfTypeAll(typeof(LanguageSource)); + // foreach (var source in sceneSources.Where(x=>!x.IsGlobalSource())) + // { + // source.SaveLanguages(true, PersistentStorage.eFileType.Streaming); + // } + // } + } +} diff --git a/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_UnloadLanguages.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_UnloadLanguages.cs.meta new file mode 100644 index 00000000..6daf1647 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/PostProcessBuild_UnloadLanguages.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 68151af3db9fb734fb3823c020c9b8c0 +timeCreated: 1489354551 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Unity XCode.meta b/UnityProject/Assets/TEngine/Editor/Localization/Unity XCode.meta new file mode 100644 index 00000000..926bf8b6 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Unity XCode.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: d9276efd8270b104ea614946e5787aa1 +folderAsset: yes +timeCreated: 1489466610 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Unity XCode/UnityEditor.iOS_I2Loc.Xcode.dll b/UnityProject/Assets/TEngine/Editor/Localization/Unity XCode/UnityEditor.iOS_I2Loc.Xcode.dll new file mode 100644 index 00000000..939cde4b Binary files /dev/null and b/UnityProject/Assets/TEngine/Editor/Localization/Unity XCode/UnityEditor.iOS_I2Loc.Xcode.dll differ diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Unity XCode/UnityEditor.iOS_I2Loc.Xcode.dll.meta b/UnityProject/Assets/TEngine/Editor/Localization/Unity XCode/UnityEditor.iOS_I2Loc.Xcode.dll.meta new file mode 100644 index 00000000..a0dda951 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Unity XCode/UnityEditor.iOS_I2Loc.Xcode.dll.meta @@ -0,0 +1,24 @@ +fileFormatVersion: 2 +guid: 6bcca352fcef3034fb01650308bf47c2 +timeCreated: 1496388333 +licenseType: Store +PluginImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + isPreloaded: 0 + platformData: + Any: + enabled: 0 + settings: {} + Editor: + enabled: 1 + settings: + DefaultValueInitialized: true + WindowsStoreApps: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Unity XCode/Xcode.txt b/UnityProject/Assets/TEngine/Editor/Localization/Unity XCode/Xcode.txt new file mode 100644 index 00000000..32eb1aed --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Unity XCode/Xcode.txt @@ -0,0 +1,9 @@ +This is a copy of the Xcode Manipulation API +https://bitbucket.org/Unity-Technologies/xcodeapi/overview + +This is already packed inside Unity under the UnityEditor.IOS.XCode namespace, +but I'm including here as well because the version that comes with unity doesn't handle +localized files. +This copy includes the pullrequest #13 (https://bitbucket.org/Unity-Technologies/xcodeapi/pull-requests/13/creation-of-variantgroup-and/diff) + +Hopefully that will be integrated in Unity soon, but until then, I will be including this files here! \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/Unity XCode/Xcode.txt.meta b/UnityProject/Assets/TEngine/Editor/Localization/Unity XCode/Xcode.txt.meta new file mode 100644 index 00000000..8562999e --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/Unity XCode/Xcode.txt.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 8f2eff1a75746804db6c0240420ebed9 +timeCreated: 1489466907 +licenseType: Store +TextScriptImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/Localization/UpgradeManager.cs b/UnityProject/Assets/TEngine/Editor/Localization/UpgradeManager.cs new file mode 100644 index 00000000..a440a252 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/UpgradeManager.cs @@ -0,0 +1,334 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace TEngine.Localization +{ + [InitializeOnLoad] + public class UpgradeManager + { + static bool mAlreadyCheckedPlugins; + + static UpgradeManager() + { + EditorApplication.update += AutoCheckPlugins; + } + + public static void AutoCheckPlugins() + { + CheckPlugins (); + } + + public static void CheckPlugins( bool bForce = false ) + { + EditorApplication.update -= AutoCheckPlugins; + + if (mAlreadyCheckedPlugins && !bForce) + return; + mAlreadyCheckedPlugins = true; + + EnablePlugins(bForce); + CreateLanguageSources(); + //CreateScriptLocalization(); + } + + const string EditorPrefs_AutoEnablePlugins = "I2Loc AutoEnablePlugins"; + + [MenuItem( "Tools/I2 Localization/Enable Plugins/Force Detection", false, 0 )] + public static void ForceCheckPlugins() + { + CheckPlugins( true ); + } + + [MenuItem( "Tools/I2 Localization/Enable Plugins/Enable Auto Detection", false, 1 )] + public static void EnableAutoCheckPlugins() + { + EditorPrefs.SetBool(EditorPrefs_AutoEnablePlugins, true); + } + [MenuItem( "Tools/I2 Localization/Enable Plugins/Enable Auto Detection", true)] + public static bool ValidEnableAutoCheckPlugins() + { + return !EditorPrefs.GetBool(EditorPrefs_AutoEnablePlugins, true); + } + + + [MenuItem( "Tools/I2 Localization/Enable Plugins/Disable Auto Detection", false, 2 )] + public static void DisableAutoCheckPlugins() + { + EditorPrefs.SetBool(EditorPrefs_AutoEnablePlugins, false); + } + [MenuItem( "Tools/I2 Localization/Enable Plugins/Disable Auto Detection", true)] + public static bool ValidDisableAutoCheckPlugins() + { + return EditorPrefs.GetBool(EditorPrefs_AutoEnablePlugins, true); + } + + + [MenuItem("Tools/I2 Localization/Toggle Highlight Localized", false, 17)] + public static void ToogleH() + { + LocalizationManager.HighlightLocalizedTargets = !LocalizationManager.HighlightLocalizedTargets; + LocalizationManager.LocalizeAll(true); + } + + + [MenuItem("Tools/I2 Localization/Create Temp")] + public static void CreateTemp() + { + LanguageSourceData source = LocalizationManager.Sources[0]; + for (int i = 0; i < 1000; ++i) + source.AddTerm("Term " + i, eTermType.Text, false); + source.UpdateDictionary(true); + } + + + + + public static void EnablePlugins( bool bForce = false ) + { + if (!bForce) + { + bool AutoEnablePlugins = EditorPrefs.GetBool(EditorPrefs_AutoEnablePlugins, true); + if (!AutoEnablePlugins) + return; + } + //var tar = System.Enum.GetValues(typeof(BuildTargetGroup)); + foreach (BuildTargetGroup target in Enum.GetValues(typeof(BuildTargetGroup))) + if (target!=BuildTargetGroup.Unknown && !target.HasAttributeOfType()) + { + #if UNITY_5_6 + if (target == BuildTargetGroup.Switch) continue; // some releases of 5.6 defined BuildTargetGroup.Switch but didn't handled it correctly + #endif + EnablePluginsOnPlatform( target ); + } + + // Force these one (iPhone has the same # than iOS and iPhone is deprecated, so iOS was been skipped) + EnablePluginsOnPlatform(BuildTargetGroup.iOS); + } + + static void EnablePluginsOnPlatform( BuildTargetGroup Platform ) + { + string Settings = PlayerSettings.GetScriptingDefineSymbolsForGroup(Platform ); + + bool HasChanged = false; + List symbols = new List( Settings.Split(';')); + + HasChanged |= UpdateSettings("NGUI", "NGUIDebug", "", ref symbols); + HasChanged |= UpdateSettings("DFGUI", "dfPanel", "", ref symbols); + HasChanged |= UpdateSettings("TK2D", "tk2dTextMesh", "", ref symbols); + HasChanged |= UpdateSettings( "TextMeshPro", "TMPro.TMP_FontAsset", "TextMeshPro", ref symbols ); + HasChanged |= UpdateSettings( "SVG", "SVGImporter.SVGAsset", "", ref symbols ); + + if (HasChanged) + { + try + { + Settings = string.Empty; + for (int i=0,imax=symbols.Count; i0) Settings += ";"; + Settings += symbols[i]; + } + PlayerSettings.SetScriptingDefineSymbolsForGroup(Platform, Settings ); + } + catch (Exception) + { + } + } + } + + static bool UpdateSettings( string mPlugin, string mType, string AssemblyType, ref List symbols) + { + try + { + bool hasPluginClass = false; + + if (!string.IsNullOrEmpty( AssemblyType )) + { + var rtype = AppDomain.CurrentDomain.GetAssemblies() + .Where( assembly => assembly.FullName.Contains(AssemblyType) ) + .Select( assembly => assembly.GetType( mType, false ) ) + .Where( t => t!=null ) + .FirstOrDefault(); + if (rtype != null) + hasPluginClass = true; + } + + if (!hasPluginClass) + hasPluginClass = typeof( Localize ).Assembly.GetType( mType, false )!=null; + + + bool hasPluginDef = symbols.IndexOf(mPlugin)>=0; + + if (hasPluginClass != hasPluginDef) + { + if (hasPluginClass) symbols.Add(mPlugin); + else symbols.Remove(mPlugin); + return true; + } + } + catch(Exception) + { + } + return false; + + } + + //[MenuItem( "Tools/I2 Localization/Create I2Languages", false, 1)] + public static LanguageSourceAsset CreateLanguageSources() + { + var globalSourcesAsset = AssetDatabase.LoadAssetAtPath(I2GlobalSourcesEditorPath); + if (globalSourcesAsset != null) + { + return globalSourcesAsset; + } + + var asset = ScriptableObject.CreateInstance(); + + var assetFolder = Application.dataPath + I2GlobalSourcesEditorFolderPath.Replace("Assets", ""); + if (!Directory.Exists(assetFolder)) + Directory.CreateDirectory(assetFolder); + + AssetDatabase.CreateAsset(asset, I2GlobalSourcesEditorPath); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + + return asset; + +#if false + if (LocalizationManager.GlobalSources==null || LocalizationManager.GlobalSources.Length==0) + return; + + Object GlobalSource = Resources.Load(LocalizationManager.GlobalSources[0]); + LanguageSourceData sourceData = null; + string sourcePath = null; + + if (GlobalSource != null) + { + if (GlobalSource is GameObject) + { + // I2Languages was a prefab before 2018.3, it should be converted to an ScriptableObject + sourcePath = AssetDatabase.GetAssetPath(GlobalSource); + LanguageSource langSourceObj = (GlobalSource as GameObject).GetComponent(); + sourceData = langSourceObj.mSource; + } + else + { + return; + } + } + + LanguageSourceAsset asset = ScriptableObject.CreateInstance(); + if (sourceData != null) + { + asset.mSource = sourceData; + AssetDatabase.DeleteAsset(sourcePath); + } + + if (string.IsNullOrEmpty(sourcePath)) + { + //string PluginPath = GetI2LocalizationPath(); + string ResourcesFolder = "Assets/Resources";//PluginPath.Substring(0, PluginPath.Length-"/Localization".Length) + "/Resources"; + + string fullresFolder = Application.dataPath + ResourcesFolder.Replace("Assets", ""); + if (!Directory.Exists(fullresFolder)) + Directory.CreateDirectory(fullresFolder); + + sourcePath = ResourcesFolder + "/" + LocalizationManager.GlobalSources[0] + ".asset"; + } + else + { + sourcePath = sourcePath.Replace(".prefab", ".asset"); + } + + AssetDatabase.CreateAsset(asset, sourcePath); + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); +#endif + } + + [MenuItem("Tools/I2 Localization/Help", false, 30)] + [MenuItem("Help/I2 Localization")] + public static void MainHelp() + { + Application.OpenURL(LocalizeInspector.HelpURL_Documentation); + } + + private const string I2GlobalSourcesEditorFolderPath = "Assets/Editor/I2Localization"; + private const string I2GlobalSourcesEditorPath = "Assets/Editor/I2Localization/I2Languages.asset"; + + [MenuItem("Tools/I2 Localization/Open I2Languages.asset", false, 0)] + public static void OpenGlobalSource() + { + var globalSourcesAsset = CreateLanguageSources(); + + if (globalSourcesAsset == null) + Debug.LogError($"没有找到数据源 {I2GlobalSourcesEditorPath}"); + + Selection.activeObject = globalSourcesAsset; +#if false + CreateLanguageSources(); + LanguageSourceAsset GO = Resources.Load(LocalizationManager.GlobalSources[0]); + if (GO == null) + Debug.Log("Unable to find Global Language at Assets/Resources/" + LocalizationManager.GlobalSources[0] + ".asset"); + + Selection.activeObject = GO; +#endif + } + + + /*static void CreateScriptLocalization() + { + string[] assets = AssetDatabase.FindAssets("ScriptLocalization"); + if (assets.Length>0) + return; + + string ScriptsFolder = "Assets"; + string ScriptText = LocalizationEditor.mScriptLocalizationHeader + " }\n}"; + + System.IO.File.WriteAllText(ScriptsFolder + "/ScriptLocalization.cs", ScriptText); + + AssetDatabase.SaveAssets(); + AssetDatabase.Refresh(); + }*/ + + public static string GetI2LocalizationPath() + { + string[] assets = AssetDatabase.FindAssets("LocalizationManager"); + if (assets.Length==0) + return string.Empty; + + string PluginPath = AssetDatabase.GUIDToAssetPath(assets[0]); + PluginPath = PluginPath.Substring(0, PluginPath.Length - "/Scripts/LocalizationManager.cs".Length); + + return PluginPath; + } + + public static string GetI2Path() + { + string pluginPath = GetI2LocalizationPath(); + return pluginPath.Substring(0, pluginPath.Length-"/Localization".Length); + } + + public static string GetI2CommonResourcesPath() + { + string I2Path = GetI2Path(); + return I2Path + "/Resources"; + } + } + + public static class UpgradeManagerHelper + { + public static bool HasAttributeOfType(this Enum enumVal) where T:Attribute + { + var type = enumVal.GetType(); + var memInfo = type.GetMember(enumVal.ToString()); + var attributes = memInfo[0].GetCustomAttributes(typeof(T), false); + return attributes.Length > 0; + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Editor/Localization/UpgradeManager.cs.meta b/UnityProject/Assets/TEngine/Editor/Localization/UpgradeManager.cs.meta new file mode 100644 index 00000000..d3ab67c2 --- /dev/null +++ b/UnityProject/Assets/TEngine/Editor/Localization/UpgradeManager.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 6e61480936111c54883dc051751e0a5f +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Editor/TEngine.Editor.asmdef b/UnityProject/Assets/TEngine/Editor/TEngine.Editor.asmdef index 97a57cae..901019b7 100644 --- a/UnityProject/Assets/TEngine/Editor/TEngine.Editor.asmdef +++ b/UnityProject/Assets/TEngine/Editor/TEngine.Editor.asmdef @@ -6,7 +6,8 @@ "GUID:e34a5702dd353724aa315fb8011f08c3", "GUID:4d1926c9df5b052469a1c63448b7609a", "GUID:6e76b07590314a543b982daed6af2509", - "GUID:2373f786d14518f44b0f475db77ba4de" + "GUID:2373f786d14518f44b0f475db77ba4de", + "GUID:6055be8ebefd69e48b49212b09b47b2f" ], "includePlatforms": [ "Editor" diff --git a/UnityProject/Assets/TEngine/Runtime/Core/Constant.meta b/UnityProject/Assets/TEngine/Runtime/Core/Constant.meta new file mode 100644 index 00000000..a3175142 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Core/Constant.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 54432698b4aa401c887a0e67d6d00cec +timeCreated: 1741755897 \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Core/Constant/Constant.cs b/UnityProject/Assets/TEngine/Runtime/Core/Constant/Constant.cs new file mode 100644 index 00000000..8ad005c6 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Core/Constant/Constant.cs @@ -0,0 +1,21 @@ +namespace TEngine +{ + /// + /// 常用设置相关常量。 + /// + public static partial class Constant + { + public static class Setting + { + public const string Language = "Setting.Language"; + public const string SoundGroupMuted = "Setting.{0}Muted"; + public const string SoundGroupVolume = "Setting.{0}Volume"; + public const string MusicMuted = "Setting.MusicMuted"; + public const string MusicVolume = "Setting.MusicVolume"; + public const string SoundMuted = "Setting.SoundMuted"; + public const string SoundVolume = "Setting.SoundVolume"; + public const string UISoundMuted = "Setting.UISoundMuted"; + public const string UISoundVolume = "Setting.UISoundVolume"; + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Core/Constant/Constant.cs.meta b/UnityProject/Assets/TEngine/Runtime/Core/Constant/Constant.cs.meta new file mode 100644 index 00000000..71aa8ea3 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Core/Constant/Constant.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 277ceef122b84924ae2fb0ac5aae5138 +timeCreated: 1741755901 \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Core/Utility/Utility.PlayerPrefs.cs b/UnityProject/Assets/TEngine/Runtime/Core/Utility/Utility.PlayerPrefs.cs index 9e8adf88..d35a3a88 100644 --- a/UnityProject/Assets/TEngine/Runtime/Core/Utility/Utility.PlayerPrefs.cs +++ b/UnityProject/Assets/TEngine/Runtime/Core/Utility/Utility.PlayerPrefs.cs @@ -265,6 +265,26 @@ { return !_enable ? defaultValue : GetString(GetUserKey(key), defaultValue); } + + /// + /// 检查是否存在指定游戏配置项。 + /// + /// 要检查游戏配置项的名称。 + /// 指定的游戏配置项是否存在。 + public static bool HasSetting(string settingName) + { + return PlayerPrefs.HasKey(settingName); + } + + /// + /// 保存游戏配置。 + /// + /// 是否保存游戏配置成功。 + public static bool Save() + { + UnityEngine.PlayerPrefs.Save(); + return true; + } } } } diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core.meta new file mode 100644 index 00000000..c4921acb --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c593d66e0920d6a43a58e3cdc76e64f6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Configurables.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Configurables.meta new file mode 100644 index 00000000..e6fb797e --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Configurables.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 8f9a3530624fd5c47a2dc16eb641ddb8 +folderAsset: yes +timeCreated: 1520745251 +licenseType: Store +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Configurables/PersistentStorage.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Configurables/PersistentStorage.cs new file mode 100644 index 00000000..fcfce870 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Configurables/PersistentStorage.cs @@ -0,0 +1,286 @@ +using System; +using System.Globalization; +using System.IO; +using System.Text; +using UnityEngine; + +namespace TEngine.Localization +{ + public static class PersistentStorage + { + static I2CustomPersistentStorage mStorage; + + public enum eFileType { Raw, Persistent, Temporal, Streaming } + + #region PlayerPrefs + public static void SetSetting_String(string key, string value) + { + if (mStorage == null) mStorage = new I2CustomPersistentStorage(); + mStorage.SetSetting_String(key, value); + } + + public static string GetSetting_String(string key, string defaultValue) + { + if (mStorage == null) mStorage = new I2CustomPersistentStorage(); + return mStorage.GetSetting_String(key, defaultValue); + } + + public static void DeleteSetting(string key) + { + if (mStorage == null) mStorage = new I2CustomPersistentStorage(); + mStorage.DeleteSetting(key); + } + + public static bool HasSetting( string key ) + { + if (mStorage == null) mStorage = new I2CustomPersistentStorage(); + return mStorage.HasSetting(key); + } + + public static void ForceSaveSettings() + { + if (mStorage == null) mStorage = new I2CustomPersistentStorage(); + mStorage.ForceSaveSettings(); + } + + #endregion + + #region File Management + + public static bool CanAccessFiles() + { + if (mStorage == null) mStorage = new I2CustomPersistentStorage(); + return mStorage.CanAccessFiles(); + } + + public static bool SaveFile(eFileType fileType, string fileName, string data, bool logExceptions = true) + { + if (mStorage == null) mStorage = new I2CustomPersistentStorage(); + return mStorage.SaveFile(fileType, fileName, data, logExceptions); + } + + public static string LoadFile(eFileType fileType, string fileName, bool logExceptions=true) + { + if (mStorage == null) mStorage = new I2CustomPersistentStorage(); + return mStorage.LoadFile(fileType, fileName, logExceptions); + } + + public static bool DeleteFile(eFileType fileType, string fileName, bool logExceptions = true) + { + if (mStorage == null) mStorage = new I2CustomPersistentStorage(); + return mStorage.DeleteFile(fileType, fileName, logExceptions); + } + + public static bool HasFile(eFileType fileType, string fileName, bool logExceptions = true) + { + if (mStorage == null) mStorage = new I2CustomPersistentStorage(); + return mStorage.HasFile(fileType, fileName, logExceptions); + } + + #endregion + } + + public abstract class I2BasePersistentStorage + { + #region PlayerPrefs + public virtual void SetSetting_String(string key, string value) + { + try + { + // Use PlayerPrefs, but if the data is bigger than the limit, split it into multiple entries + var len = value.Length; + int maxLength = 8000; + if (len<=maxLength) + { + PlayerPrefs.SetString(key, value); + } + else + { + int numSections = Mathf.CeilToInt(len / (float)maxLength); + for (int i=0; i mSpecializationsFallbacks; + + public virtual void InitializeSpecializations() + { + mSpecializations = new[] { "Any", "PC", "Touch", "Controller", "VR", + "XBox", "PS4", "PS5", "OculusVR", "ViveVR", "GearVR", "Android", "IOS", + "Switch" + }; + mSpecializationsFallbacks = new Dictionary(System.StringComparer.Ordinal) + { + { "XBox", "Controller" }, { "PS4", "Controller" }, + { "OculusVR", "VR" }, { "ViveVR", "VR" }, { "GearVR", "VR" }, + { "Android", "Touch" }, { "IOS", "Touch" } + }; + } + + public virtual string GetCurrentSpecialization() + { + if (mSpecializations == null) + InitializeSpecializations(); + + #if UNITY_ANDROID + return "Android"; + #elif UNITY_IOS + return "IOS"; + #elif UNITY_PS4 + return "PS4"; + #elif UNITY_XBOXONE + return "XBox"; + #elif UNITY_SWITCH + return "Switch"; + #elif UNITY_STANDALONE || UNITY_WEBGL + return "PC"; + #else + return (IsTouchInputSupported() ? "Touch" : "PC"); + #endif + } + + bool IsTouchInputSupported() + { + #if ENABLE_INPUT_SYSTEM + return Touchscreen.current != null; + #else + return UnityEngine.Input.touchSupported; + #endif + } + + public virtual string GetFallbackSpecialization(string specialization) + { + if (mSpecializationsFallbacks == null) + InitializeSpecializations(); + + string fallback; + if (mSpecializationsFallbacks.TryGetValue(specialization, out fallback)) + return fallback; + return "Any"; + } + } + public class SpecializationManager : BaseSpecializationManager + { + public static SpecializationManager Singleton = new SpecializationManager(); + + private SpecializationManager() + { + InitializeSpecializations(); + } + + public static string GetSpecializedText(string text, string specialization = null) + { + var idxFirst = text.IndexOf("[i2s_", StringComparison.Ordinal); + if (idxFirst < 0) + return text; + + if (string.IsNullOrEmpty(specialization)) + specialization = Singleton.GetCurrentSpecialization(); + + while (!string.IsNullOrEmpty(specialization) && specialization != "Any") + { + var tag = "[i2s_" + specialization + "]"; + int idx = text.IndexOf(tag, StringComparison.Ordinal); + if (idx < 0) + { + specialization = Singleton.GetFallbackSpecialization(specialization); + continue; + } + + idx += tag.Length; + var idxEnd = text.IndexOf("[i2s_", idx, StringComparison.Ordinal); + if (idxEnd < 0) idxEnd = text.Length; + + return text.Substring(idx, idxEnd - idx); + } + + return text.Substring(0, idxFirst); + } + + public static string SetSpecializedText(string text, string newText, string specialization) + { + if (string.IsNullOrEmpty(specialization)) + specialization = "Any"; + if ((text==null || !text.Contains("[i2s_")) && specialization=="Any") + { + return newText; + } + + var dict = GetSpecializations(text); + dict[specialization] = newText; + + return SetSpecializedText(dict); + } + + public static string SetSpecializedText( Dictionary specializations ) + { + string text; + if (!specializations.TryGetValue("Any", out text)) + text = string.Empty; + + foreach (var kvp in specializations) + { + if (kvp.Key != "Any" && !string.IsNullOrEmpty(kvp.Value)) + text += "[i2s_" + kvp.Key + "]" + kvp.Value; + } + return text; + } + + public static Dictionary GetSpecializations(string text, Dictionary buffer = null) + { + if (buffer == null) + buffer = new Dictionary(StringComparer.Ordinal); + else + buffer.Clear(); + + if (text==null) + { + buffer["Any"] = ""; + return buffer; + } + + var idxFirst = 0; + var idxEnd = text.IndexOf("[i2s_", StringComparison.Ordinal); + if (idxEnd < 0) + idxEnd=text.Length; + + buffer["Any"] = text.Substring(0, idxEnd); + idxFirst = idxEnd; + + while (idxFirst list=null) + { + if (text == null) + return; + + if (list == null) + list = new List(); + + if (!list.Contains("Any")) + list.Add("Any"); + + var idxFirst = 0; + while (idxFirst Español", GUILayout.Height(100))) StartTranslating("en", "es"); + if (GUILayout.Button("Español -> English", GUILayout.Height(100))) StartTranslating("es", "en"); + GUILayout.EndHorizontal(); + + GUILayout.Space(10); + + GUILayout.BeginHorizontal(); + GUILayout.TextArea("Multiple Translation with 1 call:\n'This is an example' -> en,zh\n'Hola' -> en"); + if (GUILayout.Button("Multi Translate", GUILayout.ExpandHeight(true))) ExampleMultiTranslations_Async(); + GUILayout.EndHorizontal(); + + + GUILayout.TextArea(TranslatedText, GUILayout.Width(Screen.width)); + + GUILayout.Space(10); + + + if (IsTranslating) + { + GUILayout.Label("Contacting Google...."); + } + } + + public void StartTranslating(string fromCode, string toCode) + { + IsTranslating = true; + + // fromCode could be "auto" to autodetect the language + GoogleTranslation.Translate(OriginalText, fromCode, toCode, OnTranslationReady); + + // can also use the ForceTranslate version: (it will block the main thread until the translation is returned) + //var translation = GoogleTranslation.ForceTranslate(OriginalText, fromCode, toCode); + //Debug.Log(translation); + } + + void OnTranslationReady(string Translation, string errorMsg) + { + IsTranslating = false; + + if (errorMsg != null) + Debug.LogError(errorMsg); + else + TranslatedText = Translation; + } + + public void ExampleMultiTranslations_Blocking() + { + // This shows how to ask for many translations + var dict = new Dictionary(); + GoogleTranslation.AddQuery("This is an example", "en", "es", dict); + GoogleTranslation.AddQuery("This is an example", "auto", "zh", dict); + GoogleTranslation.AddQuery("Hola", "es", "en", dict); + + if (!GoogleTranslation.ForceTranslate(dict)) + return; + + Debug.Log(GoogleTranslation.GetQueryResult("This is an example", "en", dict)); + Debug.Log(GoogleTranslation.GetQueryResult("This is an example", "zh", dict)); + Debug.Log(GoogleTranslation.GetQueryResult("This is an example", "", dict)); // This returns ANY translation of that text (in this case, the first one 'en') + Debug.Log(dict["Hola"].Results[0]); // example of getting the translation directly from the Results + } + + public void ExampleMultiTranslations_Async() + { + IsTranslating = true; + + // This shows how to ask for many translations + var dict = new Dictionary(); + GoogleTranslation.AddQuery("This is an example", "en", "es", dict); + GoogleTranslation.AddQuery("This is an example", "auto", "zh", dict); + GoogleTranslation.AddQuery("Hola", "es", "en", dict); + + GoogleTranslation.Translate(dict, OnMultitranslationReady); + } + + void OnMultitranslationReady(Dictionary dict, string errorMsg) + { + if (!string.IsNullOrEmpty(errorMsg)) + { + Debug.LogError(errorMsg); + return; + } + + IsTranslating = false; + TranslatedText = ""; + + TranslatedText += GoogleTranslation.GetQueryResult("This is an example", "es", dict) + "\n"; + TranslatedText += GoogleTranslation.GetQueryResult("This is an example", "zh", dict) + "\n"; + TranslatedText += GoogleTranslation.GetQueryResult("This is an example", "", dict) + "\n"; // This returns ANY translation of that text (in this case, the first one 'en') + TranslatedText += dict["Hola"].Results[0]; // example of getting the translation directly from the Results + } + + #region This methods are used in the publisher's Unity Tests + + public bool IsWaitingForTranslation() + { + return IsTranslating; + } + + public string GetTranslatedText() + { + return TranslatedText; + } + + public void SetOriginalText( string text ) + { + OriginalText = text; + } + + #endregion + + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Extension/RealTimeTranslation.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Extension/RealTimeTranslation.cs.meta new file mode 100644 index 00000000..34c932c7 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Extension/RealTimeTranslation.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c09397e14dc19fb4ab38f8216b0759a4 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: -450 + icon: {instanceID: 0} + userData: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google.meta new file mode 100644 index 00000000..fc051a48 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: dda5008126b59ad4bad4c9d2626a2345 +folderAsset: yes +timeCreated: 1461137613 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleLanguages.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleLanguages.cs new file mode 100644 index 00000000..2818240e --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleLanguages.cs @@ -0,0 +1,648 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace TEngine.Localization +{ + public enum ePluralType { Zero, One, Two, Few, Many, Plural } + + public static class GoogleLanguages + { + public static string GetLanguageCode(string Filter, bool ShowWarnings = false) + { + if (string.IsNullOrEmpty(Filter)) + return string.Empty; + + string[] Filters = Filter.ToLowerInvariant().Split(" /(),".ToCharArray()); + + foreach (var kvp in mLanguageDef) + if (LanguageMatchesFilter(kvp.Key, Filters)) + return kvp.Value.Code; + + if (ShowWarnings) + Debug.Log($"Language '{Filter}' not recognized. Please, add the language code to GoogleTranslation.cs"); + return string.Empty; + } + + + public static List GetLanguagesForDropdown(string Filter, string CodesToExclude) + { + string[] Filters = Filter.ToLowerInvariant().Split(" /(),".ToCharArray()); + + List Languages = new List(); + + foreach (var kvp in mLanguageDef) + if (string.IsNullOrEmpty(Filter) || LanguageMatchesFilter(kvp.Key, Filters)) + { + string code = string.Concat("[" + kvp.Value.Code + "]"); + if (!CodesToExclude.Contains(code)) + Languages.Add(kvp.Key + " " + code); + } + + // Add headers to variants (e.g. "English/English" before all English variants + for (int i = Languages.Count - 2; i >= 0; --i) + { + string Prefix = Languages[i].Substring(0, Languages[i].IndexOf(" [")); + if (Languages[i + 1].StartsWith(Prefix, StringComparison.Ordinal)) + { + Languages[i] = Prefix + "/" + Languages[i]; + Languages.Insert(i + 1, Prefix + "/"); + } + } + return Languages; + } + + // "Engl Unit" matches "English/United States" + static bool LanguageMatchesFilter(string Language, string[] Filters) + { + Language = Language.ToLowerInvariant(); + for (int i = 0, imax = Filters.Length; i < imax; ++i) + if (Filters[i] != "") + { + if (!Language.Contains(Filters[i].ToLower())) + return false; + Language = Language.Remove(Language.IndexOf(Filters[i], StringComparison.Ordinal), Filters[i].Length); + } + return true; + } + + + // "Arabic/Algeria [ar-XX]" returns "Arabic (Algeria)" + // "English/English [en]" returns "English" + public static string GetFormatedLanguageName(string Language) + { + string BaseLanguage = string.Empty; + + //-- Remove code -------- + int Index = Language.IndexOf(" [", StringComparison.Ordinal); + if (Index > 0) + Language = Language.Substring(0, Index); + + //-- Check for main language: "English/English [en]" returns "English" ----------- + Index = Language.IndexOf('/'); + if (Index > 0) + { + BaseLanguage = Language.Substring(0, Index); + if (Language == BaseLanguage + "/" + BaseLanguage) + return BaseLanguage; + + //-- Convert variants into right format + Language = Language.Replace("/", " (") + ")"; + } + + return Language; + } + + // English British -> "English Canada [en-CA]" + public static string GetCodedLanguage(string Language, string code) + { + string DefaultCode = GetLanguageCode(Language); + if (string.Compare(code, DefaultCode, StringComparison.OrdinalIgnoreCase) == 0) + return Language; + return string.Concat(Language, " [", code, "]"); + } + + // "English Canada [en-CA]" -> "English Canada", "en-CA" + public static void UnPackCodeFromLanguageName(string CodedLanguage, out string Language, out string code) + { + if (string.IsNullOrEmpty(CodedLanguage)) + { + Language = string.Empty; + code = string.Empty; + return; + } + int Index = CodedLanguage.IndexOf("[", StringComparison.Ordinal); + if (Index < 0) + { + Language = CodedLanguage; + code = GetLanguageCode(Language); + } + else + { + Language = CodedLanguage.Substring(0, Index).Trim(); + code = CodedLanguage.Substring(Index + 1, CodedLanguage.IndexOf("]", Index, StringComparison.Ordinal) - Index - 1); + } + } + + public static string GetGoogleLanguageCode(string InternationalCode) + { + foreach (var kvp in mLanguageDef) + if (InternationalCode == kvp.Value.Code) + { + if (kvp.Value.GoogleCode == "-") + return null; + return !string.IsNullOrEmpty(kvp.Value.GoogleCode) ? kvp.Value.GoogleCode : InternationalCode; + } + + return InternationalCode; + } + + public static string GetLanguageName(string code, bool useParenthesesForRegion=false, bool allowDiscardRegion=true) + { + foreach (var kvp in mLanguageDef) + if (code == kvp.Value.Code) + { + var langName = kvp.Key; + if (useParenthesesForRegion) + { + int idx = langName.IndexOf('/'); + if (idx > 0) + langName = langName.Substring(0, idx) + " (" + langName.Substring(idx + 1) + ")"; + } + return langName; + } + + if (allowDiscardRegion) + { + int iCode = code.IndexOf("-", StringComparison.Ordinal); + if (iCode > 0) + return GetLanguageName(code.Substring(0,iCode), useParenthesesForRegion, false); + } + return null; + } + + public static List GetAllInternationalCodes() + { + var set = new HashSet(StringComparer.Ordinal); + + foreach (var kvp in mLanguageDef) + set.Add(kvp.Value.Code); + + return new List(set); + } + + public static bool LanguageCode_HasJoinedWord(string languageCode) + { + foreach (var kvp in mLanguageDef) + if (languageCode == kvp.Value.GoogleCode || languageCode==kvp.Value.Code ) + return kvp.Value.HasJoinedWords; + + return false; + } + + public struct LanguageCodeDef + { + public string Code; // Language International Code + public string GoogleCode; // Google Translator doesn't support all languages, this is the code of closest supported language + public bool HasJoinedWords; // Some languages (e.g. Chinese, Japanese and Thai) don't add spaces to their words (all characters are placed toguether) + public int PluralRule; + } + + public static Dictionary mLanguageDef = new Dictionary(StringComparer.Ordinal) + { + /**/{"Abkhazian", new LanguageCodeDef {PluralRule=1, Code="ab", GoogleCode="-"}}, + /**/{"Afar", new LanguageCodeDef {PluralRule=1, Code="aa", GoogleCode="-"}}, + {"Afrikaans", new LanguageCodeDef {PluralRule=1, Code="af"}}, + /**/{"Akan", new LanguageCodeDef {PluralRule=1, Code="ak", GoogleCode="-"}}, + {"Albanian", new LanguageCodeDef {PluralRule=1, Code="sq"}}, + /**/{"Amharic", new LanguageCodeDef {PluralRule=1, Code="am"}}, + {"Arabic", new LanguageCodeDef {PluralRule=11, Code="ar"}}, + {"Arabic/Algeria", new LanguageCodeDef {PluralRule=11, Code="ar-DZ", GoogleCode="ar"}}, + {"Arabic/Bahrain", new LanguageCodeDef {PluralRule=11, Code="ar-BH", GoogleCode="ar"}}, + {"Arabic/Egypt", new LanguageCodeDef {PluralRule=11, Code="ar-EG", GoogleCode="ar"}}, + {"Arabic/Iraq", new LanguageCodeDef {PluralRule=11, Code="ar-IQ", GoogleCode="ar"}}, + {"Arabic/Jordan", new LanguageCodeDef {PluralRule=11, Code="ar-JO", GoogleCode="ar"}}, + {"Arabic/Kuwait", new LanguageCodeDef {PluralRule=11, Code="ar-KW", GoogleCode="ar"}}, + {"Arabic/Lebanon", new LanguageCodeDef {PluralRule=11, Code="ar-LB", GoogleCode="ar"}}, + {"Arabic/Libya", new LanguageCodeDef {PluralRule=11, Code="ar-LY", GoogleCode="ar"}}, + {"Arabic/Morocco", new LanguageCodeDef {PluralRule=11, Code="ar-MA", GoogleCode="ar"}}, + {"Arabic/Oman", new LanguageCodeDef {PluralRule=11, Code="ar-OM", GoogleCode="ar"}}, + {"Arabic/Qatar", new LanguageCodeDef {PluralRule=11, Code="ar-QA", GoogleCode="ar"}}, + {"Arabic/Saudi Arabia", new LanguageCodeDef {PluralRule=11, Code="ar-SA", GoogleCode="ar"}}, + {"Arabic/Syria", new LanguageCodeDef {PluralRule=11, Code="ar-SY", GoogleCode="ar"}}, + {"Arabic/Tunisia", new LanguageCodeDef {PluralRule=11, Code="ar-TN", GoogleCode="ar"}}, + {"Arabic/U.A.E.", new LanguageCodeDef {PluralRule=11, Code="ar-AE", GoogleCode="ar"}}, + {"Arabic/Yemen", new LanguageCodeDef {PluralRule=11, Code="ar-YE", GoogleCode="ar"}}, + /**/{"Aragonese", new LanguageCodeDef {PluralRule=1, Code="an", GoogleCode="-"}}, + {"Armenian", new LanguageCodeDef {PluralRule=1, Code="hy"}}, + /**/{"Assamese", new LanguageCodeDef {PluralRule=1, Code="as", GoogleCode="-"}}, + /**/{"Avaric", new LanguageCodeDef {PluralRule=1, Code="av", GoogleCode="-"}}, + /**/{"Avestan", new LanguageCodeDef {PluralRule=1, Code="ae", GoogleCode="-"}}, + /**/{"Aymara", new LanguageCodeDef {PluralRule=1, Code="ay", GoogleCode="-"}}, + {"Azerbaijani", new LanguageCodeDef {PluralRule=1, Code="az"}}, + /**/{"Bambara", new LanguageCodeDef {PluralRule=1, Code="bm", GoogleCode="-"}}, + /**/{"Bashkir", new LanguageCodeDef {PluralRule=1, Code="ba", GoogleCode="-"}}, + {"Basque", new LanguageCodeDef {PluralRule=1, Code="eu"}}, + {"Basque/Spain", new LanguageCodeDef {PluralRule=1, Code="eu-ES", GoogleCode="eu"}}, + {"Belarusian", new LanguageCodeDef {PluralRule=6, Code="be"}}, + /**/{"Bengali", new LanguageCodeDef {PluralRule=1, Code="bn"}}, + /**/{"Bihari", new LanguageCodeDef {PluralRule=1, Code="bh", GoogleCode="-"}}, + /**/{"Bislama", new LanguageCodeDef {PluralRule=1, Code="bi", GoogleCode="-"}}, + {"Bosnian", new LanguageCodeDef {PluralRule=6, Code="bs"}}, + /**/{"Breton", new LanguageCodeDef {PluralRule=1, Code="br", GoogleCode="-"}}, + {"Bulgariaa", new LanguageCodeDef {PluralRule=1, Code="bg"}}, + /**/{"Burmese", new LanguageCodeDef {PluralRule=1, Code="my"}}, + {"Catalan", new LanguageCodeDef {PluralRule=1, Code="ca"}}, + /**/{"Chamorro", new LanguageCodeDef {PluralRule=1, Code="ch", GoogleCode="-"}}, + /**/{"Chechen", new LanguageCodeDef {PluralRule=1, Code="ce", GoogleCode="-"}}, + /**/{"Chichewa", new LanguageCodeDef {PluralRule=1, Code="ny"}}, + {"Chinese", new LanguageCodeDef {PluralRule=0, Code="zh", GoogleCode="zh-CN", HasJoinedWords=true}}, + {"Chinese/Hong Kong", new LanguageCodeDef {PluralRule=0, Code="zh-HK", GoogleCode="zh-TW", HasJoinedWords=true}}, + {"Chinese/Macau", new LanguageCodeDef {PluralRule=0, Code="zh-MO", GoogleCode="zh-CN", HasJoinedWords=true}}, + {"Chinese/PRC", new LanguageCodeDef {PluralRule=0, Code="zh-CN", GoogleCode="zh-CN", HasJoinedWords=true}}, + {"Chinese/Simplified", new LanguageCodeDef {PluralRule=0, Code="zh-CN", GoogleCode="zh-CN", HasJoinedWords=true}}, + {"Chinese/Singapore", new LanguageCodeDef {PluralRule=0, Code="zh-SG", GoogleCode="zh-CN", HasJoinedWords=true}}, + {"Chinese/Taiwan", new LanguageCodeDef {PluralRule=0, Code="zh-TW", GoogleCode="zh-TW", HasJoinedWords=true}}, + {"Chinese/Traditional", new LanguageCodeDef {PluralRule=0, Code="zh-TW", GoogleCode="zh-TW", HasJoinedWords=true}}, + /**/{"Chuvash", new LanguageCodeDef {PluralRule=1, Code="cv", GoogleCode="-"}}, + /**/{"Cornish", new LanguageCodeDef {PluralRule=1, Code="kw", GoogleCode="-"}}, // Check plural + /**/{"Corsican", new LanguageCodeDef {PluralRule=1, Code="co"}}, + /**/{"Cree", new LanguageCodeDef {PluralRule=1, Code="cr", GoogleCode="-"}}, + {"Croatian", new LanguageCodeDef {PluralRule=6, Code="hr"}}, + {"Croatian/Bosnia and Herzegovina", new LanguageCodeDef {PluralRule=5, Code="hr-BA", GoogleCode="hr"}}, + {"Czech", new LanguageCodeDef {PluralRule=7, Code="cs"}}, + {"Danish", new LanguageCodeDef {PluralRule=1, Code="da"}}, + /**/{"Divehi", new LanguageCodeDef {PluralRule=1, Code="dv", GoogleCode="-"}}, + {"Dutch", new LanguageCodeDef {PluralRule=1, Code="nl"}}, + {"Dutch/Belgium", new LanguageCodeDef {PluralRule=1, Code="nl-BE", GoogleCode="nl"}}, + {"Dutch/Netherlands", new LanguageCodeDef {PluralRule=1, Code="nl-NL", GoogleCode="nl"}}, + /**/{"Dzongkha", new LanguageCodeDef {PluralRule=1, Code="dz", GoogleCode="-"}}, + {"English", new LanguageCodeDef {PluralRule=1, Code="en"}}, + {"English/Australia", new LanguageCodeDef {PluralRule=1, Code="en-AU", GoogleCode="en"}}, + {"English/Belize", new LanguageCodeDef {PluralRule=1, Code="en-BZ", GoogleCode="en"}}, + {"English/Canada", new LanguageCodeDef {PluralRule=1, Code="en-CA", GoogleCode="en"}}, + {"English/Caribbean", new LanguageCodeDef {PluralRule=1, Code="en-CB", GoogleCode="en"}}, + {"English/Ireland", new LanguageCodeDef {PluralRule=1, Code="en-IE", GoogleCode="en"}}, + {"English/Jamaica", new LanguageCodeDef {PluralRule=1, Code="en-JM", GoogleCode="en"}}, + {"English/New Zealand", new LanguageCodeDef {PluralRule=1, Code="en-NZ", GoogleCode="en"}}, + {"English/Republic of the Philippines", new LanguageCodeDef {PluralRule=1, Code="en-PH", GoogleCode="en"}}, + {"English/South Africa",new LanguageCodeDef {PluralRule=1, Code="en-ZA", GoogleCode="en"}}, + {"English/Trinidad", new LanguageCodeDef {PluralRule=1, Code="en-TT", GoogleCode="en"}}, + {"English/United Kingdom",new LanguageCodeDef {PluralRule=1, Code="en-GB", GoogleCode="en"}}, + {"English/United States",new LanguageCodeDef {PluralRule=1, Code="en-US", GoogleCode="en"}}, + {"English/Zimbabwe", new LanguageCodeDef {PluralRule=1, Code="en-ZW", GoogleCode="en"}}, + {"Esperanto", new LanguageCodeDef {PluralRule=1, Code="eo"}}, + {"Estonian", new LanguageCodeDef {PluralRule=1, Code="et"}}, + /**/{"Ewe", new LanguageCodeDef {PluralRule=1, Code="ee", GoogleCode="-"}}, + {"Faeroese", new LanguageCodeDef {PluralRule=1, Code="fo", GoogleCode="-"}}, + /**/{"Fijian", new LanguageCodeDef {PluralRule=1, Code="fj", GoogleCode="-"}}, + //{"Filipino", new LanguageCodeDef(){PluralRule=2, Code="tl"}}, + {"Finnish", new LanguageCodeDef {PluralRule=1, Code="fi"}}, + {"French", new LanguageCodeDef {PluralRule=2, Code="fr"}}, + {"French/Belgium", new LanguageCodeDef {PluralRule=2, Code="fr-BE", GoogleCode="fr"}}, + {"French/Canada", new LanguageCodeDef {PluralRule=2, Code="fr-CA", GoogleCode="fr"}}, + {"French/France", new LanguageCodeDef {PluralRule=2, Code="fr-FR", GoogleCode="fr"}}, + {"French/Luxembourg", new LanguageCodeDef {PluralRule=2, Code="fr-LU", GoogleCode="fr"}}, + {"French/Principality of Monaco", new LanguageCodeDef {PluralRule=2, Code="fr-MC", GoogleCode="fr"}}, + {"French/Switzerland", new LanguageCodeDef {PluralRule=2, Code="fr-CH", GoogleCode="fr"}}, + /**/{"Fulah", new LanguageCodeDef {PluralRule=1, Code="ff", GoogleCode="-"}}, + {"Galician", new LanguageCodeDef {PluralRule=1, Code="gl"}}, + {"Galician/Spain", new LanguageCodeDef {PluralRule=1, Code="gl-ES", GoogleCode="gl"}}, + {"Georgian", new LanguageCodeDef {PluralRule=0, Code="ka"}}, + {"German", new LanguageCodeDef {PluralRule=1, Code="de"}}, + {"German/Austria", new LanguageCodeDef {PluralRule=1, Code="de-AT", GoogleCode="de"}}, + {"German/Germany", new LanguageCodeDef {PluralRule=1, Code="de-DE", GoogleCode="de"}}, + {"German/Liechtenstein",new LanguageCodeDef {PluralRule=1, Code="de-LI", GoogleCode="de"}}, + {"German/Luxembourg", new LanguageCodeDef {PluralRule=1, Code="de-LU", GoogleCode="de"}}, + {"German/Switzerland", new LanguageCodeDef {PluralRule=1, Code="de-CH", GoogleCode="de"}}, + {"Greek", new LanguageCodeDef {PluralRule=1, Code="el"}}, + /**/{"Guaraní", new LanguageCodeDef {PluralRule=1, Code="gn", GoogleCode="-"}}, + {"Gujarati", new LanguageCodeDef {PluralRule=1, Code="gu"}}, + /**/{"Haitian", new LanguageCodeDef {PluralRule=1, Code="ht"}}, + /**/{"Hausa", new LanguageCodeDef {PluralRule=1, Code="ha"}}, + {"Hebrew", new LanguageCodeDef {PluralRule=1, Code="he", GoogleCode="iw"}}, + /**/{"Herero", new LanguageCodeDef {PluralRule=1, Code="hz", GoogleCode="-"}}, + {"Hindi", new LanguageCodeDef {PluralRule=1, Code="hi"}}, + /**/{"Hiri Motu", new LanguageCodeDef {PluralRule=1, Code="ho", GoogleCode="-"}}, + {"Hungarian", new LanguageCodeDef {PluralRule=1, Code="hu"}}, + /**/{"Interlingua", new LanguageCodeDef {PluralRule=1, Code="ia", GoogleCode="-"}}, + {"Indonesian", new LanguageCodeDef {PluralRule=0, Code="id"}}, + /**/{"Interlingue", new LanguageCodeDef {PluralRule=1, Code="ie", GoogleCode="-"}}, + {"Irish", new LanguageCodeDef {PluralRule=10, Code="ga"}}, + /**/{"Igbo", new LanguageCodeDef {PluralRule=1, Code="ig"}}, + /**/{"Inupiaq", new LanguageCodeDef {PluralRule=1, Code="ik", GoogleCode="-"}}, + /**/{"Ido", new LanguageCodeDef {PluralRule=1, Code="io", GoogleCode="-"}}, + {"Icelandic", new LanguageCodeDef {PluralRule=14, Code="is"}}, + {"Italian", new LanguageCodeDef {PluralRule=1, Code="it"}}, + {"Italian/Italy", new LanguageCodeDef {PluralRule=1, Code="it-IT", GoogleCode="it"}}, + {"Italian/Switzerland", new LanguageCodeDef {PluralRule=1, Code="it-CH", GoogleCode="it"}}, + /**/{"Inuktitut", new LanguageCodeDef {PluralRule=1, Code="iu", GoogleCode="-"}}, + {"Japanese", new LanguageCodeDef {PluralRule=0, Code="ja", HasJoinedWords=true}}, + /**/{"Javanese", new LanguageCodeDef {PluralRule=1, Code="jv"}}, + /**/{"Kalaallisut", new LanguageCodeDef {PluralRule=1, Code="kl", GoogleCode="-"}}, + {"Kannada", new LanguageCodeDef {PluralRule=1, Code="kn"}}, + /**/{"Kanuri", new LanguageCodeDef {PluralRule=1, Code="kr", GoogleCode="-"}}, + /**/{"Kashmiri", new LanguageCodeDef {PluralRule=1, Code="ks", GoogleCode="-"}}, + {"Kazakh", new LanguageCodeDef {PluralRule=1, Code="kk"}}, + /**/{"Central Khmer", new LanguageCodeDef {PluralRule=1, Code="km"}}, + /**/{"Kikuyu", new LanguageCodeDef {PluralRule=1, Code="ki", GoogleCode="-"}}, + /**/{"Kinyarwanda", new LanguageCodeDef {PluralRule=1, Code="rw", GoogleCode="-"}}, + /**/{"Kirghiz", new LanguageCodeDef {PluralRule=1, Code="ky"}}, + /**/{"Komi", new LanguageCodeDef {PluralRule=1, Code="kv", GoogleCode="-"}}, + /**/{"Kongo", new LanguageCodeDef {PluralRule=1, Code="kg", GoogleCode="-"}}, + {"Korean", new LanguageCodeDef {PluralRule=0, Code="ko"}}, + {"Kurdish", new LanguageCodeDef {PluralRule=1, Code="ku"}}, + /**/{"Kuanyama", new LanguageCodeDef {PluralRule=1, Code="kj", GoogleCode="-"}}, + {"Latin", new LanguageCodeDef {PluralRule=1, Code="la"}}, + /**/{"Luxembourgish", new LanguageCodeDef {PluralRule=1, Code="lb"}}, + /**/{"Ganda", new LanguageCodeDef {PluralRule=1, Code="lg", GoogleCode="-"}}, + /**/{"Limburgan", new LanguageCodeDef {PluralRule=1, Code="li", GoogleCode="-"}}, + /**/{"Lingala", new LanguageCodeDef {PluralRule=1, Code="ln", GoogleCode="-"}}, + /**/{"Lao", new LanguageCodeDef {PluralRule=1, Code="lo"}}, + {"Latvian", new LanguageCodeDef {PluralRule=5, Code="lv"}}, + /**/{"Luba-Katanga", new LanguageCodeDef {PluralRule=1, Code="lu", GoogleCode="-"}}, + {"Lithuanian", new LanguageCodeDef {PluralRule=5, Code="lt"}}, + /**/{"Manx", new LanguageCodeDef {PluralRule=1, Code="gv", GoogleCode="-"}}, + {"Macedonian", new LanguageCodeDef {PluralRule=13, Code="mk"}}, + /**/{"Malagasy", new LanguageCodeDef {PluralRule=1, Code="mg"}}, + {"Malay", new LanguageCodeDef {PluralRule=0, Code="ms"}}, + {"Malay/Brunei Darussalam", new LanguageCodeDef {PluralRule=0, Code="ms-BN", GoogleCode="ms"}}, + {"Malay/Malaysia", new LanguageCodeDef {PluralRule=0, Code="ms-MY", GoogleCode="ms"}}, + {"Malayalam", new LanguageCodeDef {PluralRule=1, Code="ml"}}, + {"Maltese", new LanguageCodeDef {PluralRule=12, Code="mt"}}, + {"Maori", new LanguageCodeDef {PluralRule=2, Code="mi"}}, + {"Marathi", new LanguageCodeDef {PluralRule=1, Code="mr"}}, + /**/{"Marshallese", new LanguageCodeDef {PluralRule=1, Code="mh", GoogleCode="-"}}, + {"Mongolian", new LanguageCodeDef {PluralRule=1, Code="mn"}}, + /**/{"Nauru", new LanguageCodeDef {PluralRule=1, Code="na", GoogleCode="-"}}, + /**/{"Navajo", new LanguageCodeDef {PluralRule=1, Code="nv", GoogleCode="-"}}, + /**/{"North Ndebele", new LanguageCodeDef {PluralRule=1, Code="nd", GoogleCode="-"}}, + /**/{"Nepali", new LanguageCodeDef {PluralRule=1, Code="ne"}}, + /**/{"Ndonga", new LanguageCodeDef {PluralRule=1, Code="ng", GoogleCode="-"}}, + {"Northern Sotho", new LanguageCodeDef {PluralRule=1, Code="ns", GoogleCode="st"}}, + {"Norwegian", new LanguageCodeDef {PluralRule=1, Code="nb", GoogleCode="no"}}, + {"Norwegian/Nynorsk", new LanguageCodeDef {PluralRule=1, Code="nn", GoogleCode="no"}}, + /**/{"Sichuan Yi", new LanguageCodeDef {PluralRule=1, Code="ii", GoogleCode="-"}}, + /**/{"South Ndebele", new LanguageCodeDef {PluralRule=1, Code="nr", GoogleCode="-"}}, + /**/{"Occitan", new LanguageCodeDef {PluralRule=1, Code="oc", GoogleCode="-"}}, + /**/{"Ojibwa", new LanguageCodeDef {PluralRule=1, Code="oj", GoogleCode="-"}}, + /**/{"Church Slavic", new LanguageCodeDef {PluralRule=1, Code="cu", GoogleCode="-"}}, + /**/{"Oromo", new LanguageCodeDef {PluralRule=1, Code="om", GoogleCode="-"}}, + /**/{"Oriya", new LanguageCodeDef {PluralRule=1, Code="or", GoogleCode="-"}}, + /**/{"Ossetian", new LanguageCodeDef {PluralRule=1, Code="os", GoogleCode="-"}}, + /**/{"Pali", new LanguageCodeDef {PluralRule=1, Code="pi", GoogleCode="-"}}, + {"Pashto", new LanguageCodeDef {PluralRule=1, Code="ps"}}, + {"Persian", new LanguageCodeDef {PluralRule=0, Code="fa"}}, + {"Polish", new LanguageCodeDef {PluralRule=8, Code="pl"}}, + {"Portuguese", new LanguageCodeDef {PluralRule=1, Code="pt"}}, + {"Portuguese/Brazil", new LanguageCodeDef {PluralRule=2, Code="pt-BR", GoogleCode="pt"}}, + {"Portuguese/Portugal", new LanguageCodeDef {PluralRule=1, Code="pt-PT", GoogleCode="pt"}}, + {"Punjabi", new LanguageCodeDef {PluralRule=1, Code="pa"}}, + {"Quechua", new LanguageCodeDef {PluralRule=1, Code="qu", GoogleCode="-"}}, + {"Quechua/Bolivia", new LanguageCodeDef {PluralRule=1, Code="qu-BO", GoogleCode="-"}}, + {"Quechua/Ecuador", new LanguageCodeDef {PluralRule=1, Code="qu-EC", GoogleCode="-"}}, + {"Quechua/Peru", new LanguageCodeDef {PluralRule=1, Code="qu-PE", GoogleCode="-"}}, + {"Rhaeto-Romanic", new LanguageCodeDef {PluralRule=1, Code="rm", GoogleCode="ro"}}, + {"Romanian", new LanguageCodeDef {PluralRule=4, Code="ro"}}, + /**/{"Rundi", new LanguageCodeDef {PluralRule=1, Code="rn", GoogleCode="-"}}, + {"Russian", new LanguageCodeDef {PluralRule=6, Code="ru"}}, + {"Russian/Republic of Moldova", new LanguageCodeDef {PluralRule=6, Code="ru-MO", GoogleCode="ru"}}, + /**/{"Sanskrit", new LanguageCodeDef {PluralRule=1, Code="sa", GoogleCode="-"}}, + /**/{"Sardinian", new LanguageCodeDef {PluralRule=1, Code="sc", GoogleCode="-"}}, + /**/{"Sindhi", new LanguageCodeDef {PluralRule=1, Code="sd"}}, + /**/{"Northern Sami", new LanguageCodeDef {PluralRule=1, Code="se", GoogleCode="-"}}, + /**/{"Samoan", new LanguageCodeDef {PluralRule=1, Code="sm"}}, + /**/{"Sango", new LanguageCodeDef {PluralRule=1, Code="sg", GoogleCode="-"}}, + {"Serbian", new LanguageCodeDef {PluralRule=6, Code="sr"}}, + {"Serbian/Bosnia and Herzegovina", new LanguageCodeDef {PluralRule=5, Code="sr-BA", GoogleCode="sr"}}, + {"Serbian/Serbia and Montenegro", new LanguageCodeDef {PluralRule=5, Code="sr-SP", GoogleCode="sr"}}, + /**/{"Scottish Gaelic", new LanguageCodeDef {PluralRule=1, Code="gd"}}, + /**/{"Shona", new LanguageCodeDef {PluralRule=1, Code="sn"}}, + /**/{"Sinhala", new LanguageCodeDef {PluralRule=1, Code="si"}}, + {"Slovak", new LanguageCodeDef {PluralRule=7, Code="sk"}}, + {"Slovenian", new LanguageCodeDef {PluralRule=9, Code="sl"}}, + /**/{"Somali", new LanguageCodeDef {PluralRule=1, Code="so"}}, + /**/{"Southern Sotho", new LanguageCodeDef {PluralRule=1, Code="st"}}, + {"Spanish", new LanguageCodeDef {PluralRule=1, Code="es"}}, + {"Spanish/Argentina", new LanguageCodeDef {PluralRule=1, Code="es-AR", GoogleCode="es"}}, + {"Spanish/Bolivia", new LanguageCodeDef {PluralRule=1, Code="es-BO", GoogleCode="es"}}, + {"Spanish/Castilian", new LanguageCodeDef {PluralRule=1, Code="es-ES", GoogleCode="es"}}, + {"Spanish/Chile", new LanguageCodeDef {PluralRule=1, Code="es-CL", GoogleCode="es"}}, + {"Spanish/Colombia", new LanguageCodeDef {PluralRule=1, Code="es-CO", GoogleCode="es"}}, + {"Spanish/Costa Rica", new LanguageCodeDef {PluralRule=1, Code="es-CR", GoogleCode="es"}}, + {"Spanish/Dominican Republic", new LanguageCodeDef {PluralRule=1, Code="es-DO", GoogleCode="es"}}, + {"Spanish/Ecuador", new LanguageCodeDef {PluralRule=1, Code="es-EC", GoogleCode="es"}}, + {"Spanish/El Salvador", new LanguageCodeDef {PluralRule=1, Code="es-SV", GoogleCode="es"}}, + {"Spanish/Guatemala", new LanguageCodeDef {PluralRule=1, Code="es-GT", GoogleCode="es"}}, + {"Spanish/Honduras", new LanguageCodeDef {PluralRule=1, Code="es-HN", GoogleCode="es"}}, + {"Spanish/Mexico", new LanguageCodeDef {PluralRule=1, Code="es-MX", GoogleCode="es"}}, + {"Spanish/Nicaragua", new LanguageCodeDef {PluralRule=1, Code="es-NI", GoogleCode="es"}}, + {"Spanish/Panama", new LanguageCodeDef {PluralRule=1, Code="es-PA", GoogleCode="es"}}, + {"Spanish/Paraguay", new LanguageCodeDef {PluralRule=1, Code="es-PY", GoogleCode="es"}}, + {"Spanish/Peru", new LanguageCodeDef {PluralRule=1, Code="es-PE", GoogleCode="es"}}, + {"Spanish/Puerto Rico", new LanguageCodeDef {PluralRule=1, Code="es-PR", GoogleCode="es"}}, + {"Spanish/Spain", new LanguageCodeDef {PluralRule=1, Code="es-ES", GoogleCode="es"}}, + {"Spanish/Uruguay", new LanguageCodeDef {PluralRule=1, Code="es-UY", GoogleCode="es"}}, + {"Spanish/Venezuela", new LanguageCodeDef {PluralRule=1, Code="es-VE", GoogleCode="es"}}, + {"Spanish/Latin Americas", new LanguageCodeDef {PluralRule=1, Code="es-US", GoogleCode="es"}}, + /**/{"Sundanese", new LanguageCodeDef {PluralRule=1, Code="su"}}, + {"Swahili", new LanguageCodeDef {Code="sw"}}, + /**/{"Swati", new LanguageCodeDef {PluralRule=1, Code="ss", GoogleCode="-"}}, + {"Swedish", new LanguageCodeDef {PluralRule=1, Code="sv"}}, + {"Swedish/Finland", new LanguageCodeDef {PluralRule=1, Code="sv-FI", GoogleCode="sv"}}, + {"Swedish/Sweden", new LanguageCodeDef {PluralRule=1, Code="sv-SE", GoogleCode="sv"}}, + {"Tamil", new LanguageCodeDef {PluralRule=1, Code="ta"}}, + {"Tatar", new LanguageCodeDef {PluralRule=0, Code="tt", GoogleCode="-"}}, + {"Telugu", new LanguageCodeDef {PluralRule=1, Code="te"}}, + /**/{"Tajik", new LanguageCodeDef {PluralRule=1, Code="tg"}}, + {"Thai", new LanguageCodeDef {PluralRule=0, Code="th", HasJoinedWords=true}}, + /**/{"Tigrinya", new LanguageCodeDef {PluralRule=1, Code="ti", GoogleCode="-"}}, + /**/{"Tibetan", new LanguageCodeDef {PluralRule=1, Code="bo", GoogleCode="-"}}, + /**/{"Turkmen", new LanguageCodeDef {PluralRule=1, Code="tk", GoogleCode="-"}}, + /**/{"Tagalog", new LanguageCodeDef {PluralRule=1, Code="tl"}}, + /**/{"Tswana", new LanguageCodeDef {PluralRule=1, Code="tn", GoogleCode="-"}}, + /**/{"Tonga", new LanguageCodeDef {PluralRule=1, Code="to", GoogleCode="-"}}, + {"Turkish", new LanguageCodeDef {PluralRule=0, Code="tr"}}, + /**/{"Tsonga", new LanguageCodeDef {PluralRule=1, Code="ts", GoogleCode="-"}}, + /**/{"Twi", new LanguageCodeDef {PluralRule=1, Code="tw", GoogleCode="-"}}, + /**/{"Tahitian", new LanguageCodeDef {PluralRule=1, Code="ty", GoogleCode="-"}}, + /**/{"Uighur", new LanguageCodeDef {PluralRule=1, Code="ug", GoogleCode="-"}}, + {"Ukrainian", new LanguageCodeDef {PluralRule=6, Code="uk"}}, + {"Urdu", new LanguageCodeDef {PluralRule=1, Code="ur"}}, + {"Uzbek", new LanguageCodeDef {PluralRule=2, Code="uz"}}, + /**/{"Venda", new LanguageCodeDef {PluralRule=1, Code="ve", GoogleCode="-"}}, + {"Vietnamese", new LanguageCodeDef {PluralRule=1, Code="vi"}}, + /**/{"Volapük", new LanguageCodeDef {PluralRule=1, Code="vo", GoogleCode="-"}}, + /**/{"Walloon", new LanguageCodeDef {PluralRule=1, Code="wa", GoogleCode="-"}}, + {"Welsh", new LanguageCodeDef {PluralRule=16, Code="cy"}}, + /**/{"Wolof", new LanguageCodeDef {PluralRule=1, Code="wo", GoogleCode="-"}}, + /**/{"Frisian", new LanguageCodeDef {PluralRule=1, Code="fy"}}, + {"Xhosa", new LanguageCodeDef {PluralRule=1, Code="xh"}}, + {"Yiddish", new LanguageCodeDef {PluralRule=1, Code="yi"}}, + /**/{"Yoruba", new LanguageCodeDef {PluralRule=1, Code="yo"}}, + /**/{"Zhuang", new LanguageCodeDef {PluralRule=1, Code="za", GoogleCode="-"}}, + {"Zulu", new LanguageCodeDef {PluralRule=1, Code="zu"}} + }; + + static int GetPluralRule( string langCode ) + { + if (langCode.Length > 2) + langCode = langCode.Substring(0, 2); + langCode = langCode.ToLower(); + + foreach (var kvp in mLanguageDef) + if (kvp.Value.Code == langCode) + { + return kvp.Value.PluralRule; + } + return 0; + } + + + //http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html + //http://cldr.unicode.org/cldr-features#TOC-Locale-specific-patterns-for-formatting-and-parsing: + //http://cldr.unicode.org/index/cldr-spec/plural-rules + public static bool LanguageHasPluralType( string langCode, string pluralType ) + { + if (pluralType == "Plural" || pluralType=="Zero" || pluralType=="One") + return true; + + int rule = GetPluralRule (langCode); + + switch (rule) + { + case 3: // Celtic (Scottish Gaelic) + return pluralType=="Two" || pluralType=="Few"; + + case 4: // Families: Romanic (Romanian) + case 5: // Families: Baltic (Latvian, Lithuanian) + case 6: // Families: Slavic (Belarusian, Bosnian, Croatian, Serbian, Russian, Ukrainian) + case 7: // Families: Slavic (Slovak, Czech) + case 8: // Families: Slavic (Polish) + return pluralType=="Few"; + + case 9: // Families: Slavic (Slovenian, Sorbian) + return pluralType=="Two" || pluralType=="Few"; + + case 10: // Families: Celtic (Irish Gaelic) + case 11: // Families: Semitic (Arabic) + case 15: // Families: Celtic (Breton) + case 16: // Families: (Welsh) + return pluralType=="Two" || pluralType=="Few" || pluralType=="Many"; + + case 12: // Families: Semitic (Maltese) + return pluralType=="Few" || pluralType=="Many"; + + case 13: // Families: Slavic (Macedonian) + return pluralType=="Two"; + } + + return false; + } + + // https://developer.mozilla.org/en-US/docs/Mozilla/Localization/Localization_and_Plurals + public static ePluralType GetPluralType( string langCode, int n ) + { + if (n == 0) return ePluralType.Zero; + if (n == 1) return ePluralType.One; + + int rule = GetPluralRule (langCode); + + switch (rule) + { + case 0: // Families: Asian (Chinese, Japanese, Korean), Persian, Turkic/Altaic (Turkish), Thai, Lao + return ePluralType.Plural; + + case 1: // Families: Germanic (Danish, Dutch, English, Faroese, Frisian, German, Norwegian, Swedish), Finno-Ugric (Estonian, Finnish, Hungarian), Language isolate (Basque), Latin/Greek (Greek), Semitic (Hebrew), Romanic (Italian, Portuguese, Spanish, Catalan), Vietnamese + return n==1 ? ePluralType.One : ePluralType.Plural; + + case 2: // Families: Romanic (French, Brazilian Portuguese) + return n<=1 ? ePluralType.One : ePluralType.Plural; + + case 3: // Celtic (Scottish Gaelic) + return n==1 || n==11 ? ePluralType.One : + n==2 || n==12 ? ePluralType.Two : + inRange(n,3,10) || inRange(n,13,19) ? ePluralType.Few : ePluralType.Plural; + + case 4: // Families: Romanic (Romanian) + return n==1 ? ePluralType.One : + inRange(n%100, 1, 19) ? ePluralType.Few : ePluralType.Plural; + + case 5: // Families: Baltic (Latvian, Lithuanian) + return n%10==1 && n%100!=11 ? ePluralType.One : + n%10>=2 && (n%100<10 || n%100>=20) ? ePluralType.Few : ePluralType.Plural; + + case 6: // Families: Slavic (Belarusian, Bosnian, Croatian, Serbian, Russian, Ukrainian) + return n % 10 == 1 && n % 100 != 11 ? ePluralType.One : + inRange (n%10,2,4) && !inRange (n%100,12,14) ? ePluralType.Few : ePluralType.Plural; + + case 7: // Families: Slavic (Slovak, Czech) + return n==1 ? ePluralType.One : + inRange(n,2,4) ? ePluralType.Few : ePluralType.Plural; + + case 8: // Families: Slavic (Polish) + return n==1 ? ePluralType.One : + inRange (n%10,2,4) && !inRange (n%100,12,14) ? ePluralType.Few : ePluralType.Plural; + + case 9: // Families: Slavic (Slovenian, Sorbian) + return n%100==1 ? ePluralType.One : + n%100==2 ? ePluralType.Two : + inRange(n%100,3,4) ? ePluralType.Few : ePluralType.Plural; + + case 10: // Families: Celtic (Irish Gaelic) + return n==1 ? ePluralType.One : + n==2 ? ePluralType.Two : + inRange(n, 3,6) ? ePluralType.Few : + inRange(n, 7,10)? ePluralType.Many : ePluralType.Plural; + + case 11: // Families: Semitic (Arabic) + return n==0 ? ePluralType.Zero : + n==1 ? ePluralType.One : + n==2 ? ePluralType.Two : + inRange(n%100,3,10) ? ePluralType.Few : + n%100>=11 ? ePluralType.Many : ePluralType.Plural; + + case 12: // Families: Semitic (Maltese) + return n==1 ? ePluralType.One : + inRange(n%100, 1, 10) ? ePluralType.Few : + inRange(n%100, 11,19) ? ePluralType.Many : ePluralType.Plural; + + case 13: // Families: Slavic (Macedonian) + return n % 10 == 1 ? ePluralType.One : + n % 10 == 2 ? ePluralType.Two : ePluralType.Plural; + + case 14: // Plural rule #15 (2 forms) + return n%10==1 && n%100!=11 ? ePluralType.One : ePluralType.Plural; + + case 15: // Families: Celtic (Breton) + return n % 10 == 1 && n % 100 != 11 && n % 100 != 71 && n % 100 != 91 ? ePluralType.One : + n % 10 == 2 && n % 100 != 12 && n % 100 != 72 && n % 100 != 92 ? ePluralType.Two : + (n % 10 == 3 || n % 10 == 4 || n % 10 == 9) && n % 100 != 13 && n % 100 != 14 && n % 100 != 19 && n % 100 != 73 && n % 100 != 74 && n % 100 != 79 && n % 100 != 93 && n % 100 != 94 && n % 100 != 99 ? ePluralType.Few : + n%1000000==0 ? ePluralType.Many : ePluralType.Plural; + + case 16: // Families: (Welsh) + return n==0 ? ePluralType.Zero : + n==1 ? ePluralType.One : + n==2 ? ePluralType.Two : + n==3 ? ePluralType.Few : + n==6 ? ePluralType.Many : ePluralType.Plural; + + } + + return ePluralType.Plural; + } + + // A number that belong to the pluralType form + public static int GetPluralTestNumber( string langCode, ePluralType pluralType ) + { + switch (pluralType) + { + case ePluralType.Zero: + return 0; + + case ePluralType.One: + return 1; + + case ePluralType.Few: + return 3; + + case ePluralType.Many: + { + int rule = GetPluralRule (langCode); + if (rule == 10) return 8; + if (rule == 11 || rule==12) return 13; + if (rule == 15) return 1000000; + return 6; + } + + default: + return 936; + } + } + + static bool inRange(int amount, int min, int max) + { + return amount >= min && amount <= max; + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleLanguages.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleLanguages.cs.meta new file mode 100644 index 00000000..d4aa9145 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleLanguages.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 26e29b3e77176de4cbb64a3ec85beee6 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleTranslation.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleTranslation.cs new file mode 100644 index 00000000..cf30c1ea --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleTranslation.cs @@ -0,0 +1,86 @@ +using System.Collections.Generic; + +namespace TEngine.Localization +{ + using TranslationDictionary = Dictionary; + + public static partial class GoogleTranslation + { + public delegate void fnOnTranslated(string Translation, string Error); + + public static bool CanTranslate () + { + return LocalizationManager.Sources.Count > 0 && + !string.IsNullOrEmpty (LocalizationManager.GetWebServiceURL()); + } + + + // LanguageCodeFrom can be "auto" + // After the translation is returned from Google, it will call OnTranslationReady(TranslationResult, ErrorMsg) + // TranslationResult will be null if translation failed + public static void Translate( string text, string LanguageCodeFrom, string LanguageCodeTo, fnOnTranslated OnTranslationReady ) + { + LocalizationManager.InitializeIfNeeded(); + if (!CanTranslate()) + { + OnTranslationReady(null, "WebService is not set correctly or needs to be reinstalled"); + return; + } + //LanguageCodeTo = GoogleLanguages.GetGoogleLanguageCode(LanguageCodeTo); + + if (LanguageCodeTo==LanguageCodeFrom) + { + OnTranslationReady(text, null); + return; + } + + TranslationDictionary queries = new TranslationDictionary(System.StringComparer.Ordinal); + + + // Unsupported language + if (string.IsNullOrEmpty(LanguageCodeTo)) + { + OnTranslationReady(string.Empty, null); + return; + } + CreateQueries(text, LanguageCodeFrom, LanguageCodeTo, queries); // can split plurals and specializations into several queries + + Translate(queries, (results,error)=> + { + if (!string.IsNullOrEmpty(error) || results.Count==0) + { + OnTranslationReady(null, error); + return; + } + + string result = RebuildTranslation( text, queries, LanguageCodeTo); // gets the result from google and rebuilds the text from multiple queries if its is plurals + OnTranslationReady( result, null ); + }); + } + + // Query google for the translation and waits until google returns + // On some Unity versions (e.g. 2017.1f1) unity doesn't handle well waiting for WWW in the main thread, so this call can fail + // In those cases, its advisable to use the Async version (GoogleTranslation.Translate(....)) + public static string ForceTranslate ( string text, string LanguageCodeFrom, string LanguageCodeTo ) + { + TranslationDictionary dict = new TranslationDictionary(System.StringComparer.Ordinal); + AddQuery(text, LanguageCodeFrom, LanguageCodeTo, dict); + + var job = new TranslationJob_Main(dict, null); + while (true) + { + var state = job.GetState(); + if (state == TranslationJob.eJobState.Running) + continue; + + if (state == TranslationJob.eJobState.Failed) + return null; + + //TranslationJob.eJobState.Succeeded + return GetQueryResult( text, "", dict); + } + } + + } +} + diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleTranslation.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleTranslation.cs.meta new file mode 100644 index 00000000..4acd6858 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleTranslation.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: cc4d0073f9e452047bd31b01de2bbd82 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleTranslation_Post.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleTranslation_Post.cs new file mode 100644 index 00000000..0d9e958e --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleTranslation_Post.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine.Networking; + +namespace TEngine.Localization +{ + using TranslationDictionary = Dictionary; + + + public static partial class GoogleTranslation + { + static List mCurrentTranslations = new List(); + static List mTranslationJobs = new List(); + + public delegate void fnOnTranslationReady(TranslationDictionary dict, string error); + +#region Multiple Translations + + public static void Translate( TranslationDictionary requests, fnOnTranslationReady OnTranslationReady, bool usePOST = true ) + { + //WWW www = GetTranslationWWW( requests, usePOST ); + //I2.Loc.CoroutineManager.Start(WaitForTranslation(www, OnTranslationReady, requests)); + AddTranslationJob( new TranslationJob_Main(requests, OnTranslationReady) ); + } + + public static bool ForceTranslate(TranslationDictionary requests, bool usePOST = true) + { + var job = new TranslationJob_Main(requests, null); + while (true) + { + var state = job.GetState(); + if (state == TranslationJob.eJobState.Running) + continue; + + if (state == TranslationJob.eJobState.Failed) + return false; + + //TranslationJob.eJobState.Succeeded + return true; + } + } + + public static List ConvertTranslationRequest(TranslationDictionary requests, bool encodeGET) + { + List results = new List(); + var sb = new StringBuilder(); + + foreach (var kvp in requests) + { + var request = kvp.Value; + if (sb.Length > 0) + sb.Append(""); + + sb.Append(GoogleLanguages.GetGoogleLanguageCode(request.LanguageCode)); + sb.Append(":"); + for (int i = 0; i < request.TargetLanguagesCode.Length; ++i) + { + if (i != 0) sb.Append(","); + sb.Append(GoogleLanguages.GetGoogleLanguageCode(request.TargetLanguagesCode[i])); + } + sb.Append("="); + + var text = TitleCase(request.Text) == request.Text ? request.Text.ToLowerInvariant() : request.Text; + + if (!encodeGET) + { + sb.Append(text); + } + else + { + sb.Append(Uri.EscapeDataString(text)); + if (sb.Length > 4000) + { + results.Add(sb.ToString()); + sb.Length = 0; + } + } + } + results.Add(sb.ToString()); + return results; + } + + static void AddTranslationJob( TranslationJob job ) + { + mTranslationJobs.Add(job); + if (mTranslationJobs.Count==1) + { + CoroutineManager.Start(WaitForTranslations()); + } + } + + static IEnumerator WaitForTranslations() + { + while (mTranslationJobs.Count > 0) + { + var jobs = mTranslationJobs.ToArray(); + foreach (var job in jobs) + { + if (job.GetState() != TranslationJob.eJobState.Running) + mTranslationJobs.Remove(job); + } + yield return null; + } + } + + + + public static string ParseTranslationResult( string html, TranslationDictionary requests ) + { + //Debug.Log(html); + // Handle google restricting the webservice to run + if (html.StartsWith("") || html.StartsWith("")) + { + if (html.Contains("The script completed but did not return anything")) + return "The current Google WebService is not supported.\nPlease, delete the WebService from the Google Drive and Install the latest version."; + if (html.Contains("Service invoked too many times in a short time")) + return ""; // ignore and try again + return "There was a problem contacting the WebService. Please try again later\n" + html; + } + + string[] texts = html.Split (new[]{""}, StringSplitOptions.None); + string[] splitter = {""}; + int i = 0; + + var Keys = requests.Keys.ToArray(); + foreach (var text in Keys) + { + var temp = FindQueryFromOrigText(text, requests); + var fullText = texts[i++]; + if (temp.Tags != null) + { + //for (int j = 0, jmax = temp.Tags.Length; j < jmax; ++j) + for (int j = temp.Tags.Length-1; j>=0; --j) + { + fullText = fullText.Replace(GetGoogleNoTranslateTag(j), temp.Tags[j]); + //fullText = fullText.Replace( /*"{[" + j + "]}"*/ ((char)(0x2600+j)).ToString(), temp.Tags[j]); + } + } + + temp.Results = fullText.Split (splitter, StringSplitOptions.None); + + // Google has problem translating this "This Is An Example" but not this "this is an example" + if (TitleCase(text)==text) + { + for (int j=0; j0 || mTranslationJobs.Count > 0; + } + + public static void CancelCurrentGoogleTranslations() + { + mCurrentTranslations.Clear (); + foreach (var job in mTranslationJobs) + { + job.Dispose(); + } + mTranslationJobs.Clear(); + } + +#endregion + } +} + diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleTranslation_Post.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleTranslation_Post.cs.meta new file mode 100644 index 00000000..61a1aeb2 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleTranslation_Post.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 94b94d27ab6931c4abee1e4d3655b660 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleTranslation_Queries.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleTranslation_Queries.cs new file mode 100644 index 00000000..9d3af139 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/GoogleTranslation_Queries.cs @@ -0,0 +1,375 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace TEngine.Localization +{ + using TranslationDictionary = Dictionary; + + public struct TranslationQuery + { + public string OrigText; + public string Text; // Text without Tags + public string LanguageCode; + public string[] TargetLanguagesCode; + public string[] Results; // This is filled google returns the translations + public string[] Tags; // This are the sections of the orignal text that shoudn't be translated + } + + public static partial class GoogleTranslation + { + public static void CreateQueries(string text, string LanguageCodeFrom, string LanguageCodeTo, TranslationDictionary dict) + { + if (!text.Contains("[i2s_")) + { + CreateQueries_Plurals(text, LanguageCodeFrom, LanguageCodeTo, dict); + return; + } + + var variants = SpecializationManager.GetSpecializations(text); + foreach (var kvp in variants) + { + CreateQueries_Plurals(kvp.Value, LanguageCodeFrom, LanguageCodeTo, dict); + } + } + + static void CreateQueries_Plurals(string text, string LanguageCodeFrom, string LanguageCodeTo, TranslationDictionary dict) + { + bool hasPluralParams = text.Contains("{[#"); + bool hasPluralTypes = text.Contains("[i2p_"); + if (!HasParameters(text) || !hasPluralParams && !hasPluralTypes) + { + AddQuery(text, LanguageCodeFrom, LanguageCodeTo, dict); + return; + } + + bool forcePluralParam = hasPluralParams; + + for (var i = (ePluralType)0; i <= ePluralType.Plural; ++i) + { + var pluralType = i.ToString(); + if (!GoogleLanguages.LanguageHasPluralType(LanguageCodeTo, pluralType)) + continue; + + var newText = GetPluralText(text, pluralType); + int testNumber = GoogleLanguages.GetPluralTestNumber(LanguageCodeTo, i); + + var parameter = GetPluralParameter(newText, forcePluralParam); + if (!string.IsNullOrEmpty(parameter)) + newText = newText.Replace(parameter, testNumber.ToString()); + + //Debug.Log("Translate: " + newText); + + AddQuery(newText, LanguageCodeFrom, LanguageCodeTo, dict); + } + } + + public static void AddQuery(string text, string LanguageCodeFrom, string LanguageCodeTo, TranslationDictionary dict) + { + if (string.IsNullOrEmpty(text)) + return; + + + if (!dict.ContainsKey(text)) + { + var query = new TranslationQuery { OrigText = text, LanguageCode = LanguageCodeFrom, TargetLanguagesCode = new[] { LanguageCodeTo } }; + query.Text = text; + ParseNonTranslatableElements(ref query); + dict[text] = query; + } + else + { + var query = dict[text]; + if (Array.IndexOf(query.TargetLanguagesCode, LanguageCodeTo) < 0) + { + query.TargetLanguagesCode = query.TargetLanguagesCode.Concat(new[] { LanguageCodeTo }).Distinct().ToArray(); + } + dict[text] = query; + } + } + + static string GetTranslation(string text, string LanguageCodeTo, TranslationDictionary dict) + { + if (!dict.ContainsKey(text)) + return null; + var query = dict[text]; + + int langIdx = Array.IndexOf(query.TargetLanguagesCode, LanguageCodeTo); + if (langIdx < 0) + return ""; + + if (query.Results == null) + return ""; + return query.Results[langIdx]; + } + + static TranslationQuery FindQueryFromOrigText(string origText, TranslationDictionary dict) + { + foreach (var kvp in dict) + { + if (kvp.Value.OrigText == origText) + return kvp.Value; + } + return default(TranslationQuery); + } + + public static bool HasParameters( string text ) + { + int idx = text.IndexOf("{[", StringComparison.Ordinal); + if (idx < 0) return false; + return text.IndexOf("]}", idx, StringComparison.Ordinal) > 0; + } + + public static string GetPluralParameter(string text, bool forceTag) // force tag requires that the parameter has the form {[#param]} + { + // Try finding the "plural parameter" that has the form {[#name]} + // this allows using the second parameter as plural: "Player {[name1]} has {[#value]} points" + int idx0 = text.IndexOf("{[#", StringComparison.Ordinal); + if (idx0 < 0) + { + if (forceTag) return null; + idx0 = text.IndexOf("{[", StringComparison.Ordinal); // fallback to the first paremeter if no one has the # tag + } + if (idx0 < 0) + return null; + + int idx1 = text.IndexOf("]}", idx0+2, StringComparison.Ordinal); + if (idx1 < 0) + return null; // no closing parameter tag + + return text.Substring(idx0, idx1 - idx0 + 2); + } + + public static string GetPluralText( string text, string pluralType ) + { + pluralType = "[i2p_" + pluralType + "]"; + int idx0 = text.IndexOf(pluralType, StringComparison.Ordinal); + if (idx0>=0) + { + idx0 += pluralType.Length; + int idx1 = text.IndexOf("[i2p_",idx0, StringComparison.Ordinal); + if (idx1 < 0) idx1 = text.Length; + + return text.Substring(idx0, idx1 - idx0); + } + + // PluralType not found, fallback to the first one + idx0 = text.IndexOf("[i2p_", StringComparison.Ordinal); + if (idx0 < 0) + return text; // No plural tags: "my text" + + if (idx0>0) + return text.Substring(0, idx0); // Case: "my text[i2p_zero]hello" + + // Case: "[i2p_plural]my text[i2p_zero]hello" + idx0 = text.IndexOf("]", StringComparison.Ordinal); + if (idx0 < 0) return text; // starts like a plural, but has none + + idx0 += 1; + int idx2 = text.IndexOf("[i2p_", idx0, StringComparison.Ordinal); + if (idx2 < 0) idx2 = text.Length; + return text.Substring(idx0, idx2 - idx0); + } + + static int FindClosingTag(string tag, MatchCollection matches, int startIndex) + { + for (int i = startIndex, imax = matches.Count; i < imax; ++i) + { + var newTag = I2Utils.GetCaptureMatch(matches[i]); + if (newTag[0]=='/' && tag.StartsWith(newTag.Substring(1), StringComparison.Ordinal)) + return i; + } + return -1; + } + + static string GetGoogleNoTranslateTag(int tagNumber) + { + //return " I2NT" + tagNumber; + if (tagNumber < 70) + return "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++".Substring(0, tagNumber+1); + + string s = ""; + for (int i = -1; i < tagNumber; ++i) + s += "+"; + return s; + } + + + static void ParseNonTranslatableElements( ref TranslationQuery query ) + { + //\[i2nt].*\[\/i2nt] + var matches = Regex.Matches( query.Text, @"\{\[(.*?)]}|\[(.*?)]|\<(.*?)>"); + if (matches == null || matches.Count == 0) + return; + + string finalText = query.Text; + List finalTags = new List(); + for (int i=0, imax=matches.Count; i + + if (iClosingTag < 0) + { + // Its not a tag, its a parameter + var fulltag = matches[i].ToString(); + if (fulltag.StartsWith("{[", StringComparison.Ordinal) && fulltag.EndsWith("]}", StringComparison.Ordinal)) + { + finalText = finalText.Replace(fulltag, GetGoogleNoTranslateTag(finalTags.Count)+" "); // 0x2600 is the start of the UNICODE Miscellaneous Symbols table, so they are not going to be translated by google + //finalText = finalText.Replace(fulltag, /*"{[" + finalTags.Count + "]}"*/ ((char)(0x2600 + finalTags.Count)).ToString()); // 0x2600 is the start of the UNICODE Miscellaneous Symbols table, so they are not going to be translated by google + finalTags.Add(fulltag); + } + continue; + } + + if (tag == "i2nt") + { + var tag1 = query.Text.Substring(matches[i].Index, matches[iClosingTag].Index-matches[i].Index + matches[iClosingTag].Length); + finalText = finalText.Replace(tag1, GetGoogleNoTranslateTag(finalTags.Count)+" "); + //finalText = finalText.Replace(tag1, /*"{[" + finalTags.Count + "]}"*/ ((char)(0x2600 + finalTags.Count)).ToString()); + + finalTags.Add(tag1); + } + else + { + var tag1 = matches[i].ToString(); + finalText = finalText.Replace(tag1, GetGoogleNoTranslateTag(finalTags.Count)+" "); + //finalText = finalText.Replace(tag1, /*"{[" + finalTags.Count + "]}"*/ ((char)(0x2600 + finalTags.Count)).ToString()); + finalTags.Add(tag1); + + var tag2 = matches[iClosingTag].ToString(); + finalText = finalText.Replace(tag2, GetGoogleNoTranslateTag(finalTags.Count)+" "); + //finalText = finalText.Replace(tag2, /*"{[" + finalTags.Count + "]}"*/ ((char)(0x2600 + finalTags.Count)).ToString()); + finalTags.Add(tag2); + } + } + + query.Text = finalText; + query.Tags = finalTags.ToArray(); + } + + public static string GetQueryResult(string text, string LanguageCodeTo, TranslationDictionary dict) + { + if (!dict.ContainsKey(text)) + return null; + + var query = dict[text]; + if (query.Results == null || query.Results.Length < 0) + return null; + + if (string.IsNullOrEmpty(LanguageCodeTo)) + return query.Results[0]; + + int idx = Array.IndexOf(query.TargetLanguagesCode, LanguageCodeTo); + if (idx < 0) + return null; + + return query.Results[idx]; + } + + public static string RebuildTranslation(string text, TranslationDictionary dict, string LanguageCodeTo) + { + if (!text.Contains("[i2s_")) + { + return RebuildTranslation_Plural(text, dict, LanguageCodeTo); + } + + var variants = SpecializationManager.GetSpecializations(text); + var results = new Dictionary(StringComparer.Ordinal); + + foreach (var kvp in variants) + { + results[kvp.Key] = RebuildTranslation_Plural(kvp.Value, dict, LanguageCodeTo); + } + return SpecializationManager.SetSpecializedText(results); + } + + static string RebuildTranslation_Plural( string text, TranslationDictionary dict, string LanguageCodeTo ) + { + bool hasPluralParams = text.Contains("{[#"); + bool hasPluralTypes = text.Contains("[i2p_"); + if (!HasParameters(text) || !hasPluralParams && !hasPluralTypes) + { + return GetTranslation (text, LanguageCodeTo, dict); + } + + var sb = new StringBuilder(); + + string pluralTranslation = null; + bool forcePluralParam = hasPluralParams; + + + for (var i = ePluralType.Plural; i >= 0; --i) + { + var pluralType = i.ToString(); + if (!GoogleLanguages.LanguageHasPluralType(LanguageCodeTo, pluralType)) + continue; + + var newText = GetPluralText(text, pluralType); + int testNumber = GoogleLanguages.GetPluralTestNumber(LanguageCodeTo, i); + + var parameter = GetPluralParameter(newText, forcePluralParam); + if (!string.IsNullOrEmpty(parameter)) + newText = newText.Replace(parameter, testNumber.ToString()); + + var translation = GetTranslation(newText, LanguageCodeTo, dict); + //Debug.LogFormat("from: {0} -> {1}", newText, translation); + if (!string.IsNullOrEmpty(parameter)) + translation = translation.Replace(testNumber.ToString(), parameter); + + if (i==ePluralType.Plural) + { + pluralTranslation = translation; + } + else + { + if (translation == pluralTranslation) + continue; + sb.AppendFormat("[i2p_{0}]", pluralType); + } + sb.Append(translation); + } + + return sb.ToString (); + } + + + + public static string UppercaseFirst(string s) + { + if (string.IsNullOrEmpty(s)) + { + return string.Empty; + } + char[] a = s.ToLower().ToCharArray(); + a[0] = char.ToUpper(a[0]); + return new string(a); + } + public static string TitleCase(string s) + { + if (string.IsNullOrEmpty(s)) + { + return string.Empty; + } + +#if NETFX_CORE + var sb = new StringBuilder(s); + sb[0] = char.ToUpper(sb[0]); + for (int i = 1, imax=s.Length; i Childs { get { yield break;} } + public IEnumerable DeepChilds + { + get + { + foreach (var C in Childs) + foreach (var D in C.DeepChilds) + yield return D; + } + } + + public override string ToString() + { + return "JSONNode"; + } + public virtual string ToString(string aPrefix) + { + return "JSONNode"; + } + + #endregion common interface + + #region typecasting properties + public virtual int AsInt + { + get + { + int v = 0; + if (int.TryParse(Value,out v)) + return v; + return 0; + } + set + { + Value = value.ToString(); + } + } + public virtual float AsFloat + { + get + { + float v = 0.0f; + if (float.TryParse(Value,out v)) + return v; + return 0.0f; + } + set + { + Value = value.ToString(); + } + } + public virtual double AsDouble + { + get + { + double v = 0.0; + if (double.TryParse(Value,out v)) + return v; + return 0.0; + } + set + { + Value = value.ToString(); + } + } + public virtual bool AsBool + { + get + { + bool v = false; + if (bool.TryParse(Value,out v)) + return v; + return !string.IsNullOrEmpty(Value); + } + set + { + Value = value?"true":"false"; + } + } + public virtual JSONArray AsArray + { + get + { + return this as JSONArray; + } + } + public virtual JSONClass AsObject + { + get + { + return this as JSONClass; + } + } + + + #endregion typecasting properties + + #region operators + public static implicit operator JSONNode(string s) + { + return new JSONData(s); + } + public static implicit operator string(JSONNode d) + { + return d == null?null:d.Value; + } + public static bool operator ==(JSONNode a, object b) + { + if (b == null && a is JSONLazyCreator) + return true; + return ReferenceEquals(a,b); + } + + public static bool operator !=(JSONNode a, object b) + { + return !(a == b); + } + public override bool Equals (object obj) + { + return ReferenceEquals(this, obj); + } + public override int GetHashCode () + { + return base.GetHashCode(); + } + + + #endregion operators + + internal static string Escape(string aText) + { + string result = ""; + foreach(char c in aText) + { + switch(c) + { + case '\\' : result += "\\\\"; break; + case '\"' : result += "\\\""; break; + case '\n' : result += "\\n" ; break; + case '\r' : result += "\\r" ; break; + case '\t' : result += "\\t" ; break; + case '\b' : result += "\\b" ; break; + case '\f' : result += "\\f" ; break; + default : result += c ; break; + } + } + return result; + } + + public static JSONNode Parse(string aJSON) + { + Stack stack = new Stack(); + JSONNode ctx = null; + int i = 0; + string Token = ""; + string TokenName = ""; + bool QuoteMode = false; + while (i < aJSON.Length) + { + switch (aJSON[i]) + { + case '{': + if (QuoteMode) + { + Token += aJSON[i]; + break; + } + stack.Push(new JSONClass()); + if (ctx != null) + { + TokenName = TokenName.Trim(); + if (ctx is JSONArray) + ctx.Add(stack.Peek()); + else if (TokenName != "") + ctx.Add(TokenName,stack.Peek()); + } + TokenName = ""; + Token = ""; + ctx = stack.Peek(); + break; + + case '[': + if (QuoteMode) + { + Token += aJSON[i]; + break; + } + + stack.Push(new JSONArray()); + if (ctx != null) + { + TokenName = TokenName.Trim(); + if (ctx is JSONArray) + ctx.Add(stack.Peek()); + else if (TokenName != "") + ctx.Add(TokenName,stack.Peek()); + } + TokenName = ""; + Token = ""; + ctx = stack.Peek(); + break; + + case '}': + case ']': + if (QuoteMode) + { + Token += aJSON[i]; + break; + } + if (stack.Count == 0) + throw new Exception("JSON Parse: Too many closing brackets"); + + stack.Pop(); + if (Token != "") + { + TokenName = TokenName.Trim(); + if (ctx is JSONArray) + ctx.Add(Token); + else if (TokenName != "") + ctx.Add(TokenName,Token); + } + TokenName = ""; + Token = ""; + if (stack.Count>0) + ctx = stack.Peek(); + break; + + case ':': + if (QuoteMode) + { + Token += aJSON[i]; + break; + } + TokenName = Token; + Token = ""; + break; + + case '"': + QuoteMode ^= true; + break; + + case ',': + if (QuoteMode) + { + Token += aJSON[i]; + break; + } + if (Token != "") + { + if (ctx is JSONArray) + ctx.Add(Token); + else if (TokenName != "") + ctx.Add(TokenName, Token); + } + TokenName = ""; + Token = ""; + break; + + case '\r': + case '\n': + break; + + case ' ': + case '\t': + if (QuoteMode) + Token += aJSON[i]; + break; + + case '\\': + ++i; + if (QuoteMode) + { + char C = aJSON[i]; + switch (C) + { + case 't' : Token += '\t'; break; + case 'r' : Token += '\r'; break; + case 'n' : Token += '\n'; break; + case 'b' : Token += '\b'; break; + case 'f' : Token += '\f'; break; + case 'u': + { + string s = aJSON.Substring(i+1,4); + Token += (char)int.Parse(s, NumberStyles.AllowHexSpecifier); + i += 4; + break; + } + default : Token += C; break; + } + } + break; + + default: + Token += aJSON[i]; + break; + } + ++i; + } + if (QuoteMode) + { + throw new Exception("JSON Parse: Quotation marks seems to be messed up."); + } + return ctx; + } + + public virtual void Serialize(BinaryWriter aWriter) {} + + public void SaveToStream(Stream aData) + { + var W = new BinaryWriter(aData); + Serialize(W); + } + + #if USE_SharpZipLib + public void SaveToCompressedStream(System.IO.Stream aData) + { + using (var gzipOut = new ICSharpCode.SharpZipLib.BZip2.BZip2OutputStream(aData)) + { + gzipOut.IsStreamOwner = false; + SaveToStream(gzipOut); + gzipOut.Close(); + } + } + + public void SaveToCompressedFile(string aFileName) + { + #if USE_FileIO + System.IO.Directory.CreateDirectory((new System.IO.FileInfo(aFileName)).Directory.FullName); + using(var F = System.IO.File.OpenWrite(aFileName)) + { + SaveToCompressedStream(F); + } + #else + throw new Exception("Can't use File IO stuff in webplayer"); + #endif + } + public string SaveToCompressedBase64() + { + using (var stream = new System.IO.MemoryStream()) + { + SaveToCompressedStream(stream); + stream.Position = 0; + return System.Convert.ToBase64String(stream.ToArray()); + } + } + + #else + public void SaveToCompressedStream(Stream aData) + { + throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); + } + public void SaveToCompressedFile(string aFileName) + { + throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); + } + public string SaveToCompressedBase64() + { + throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); + } + #endif + + public void SaveToFile(string aFileName) + { + #if USE_FileIO + Directory.CreateDirectory(new FileInfo(aFileName).Directory.FullName); + using(var F = File.OpenWrite(aFileName)) + { + SaveToStream(F); + } + #else + throw new Exception("Can't use File IO stuff in webplayer"); + #endif + } + public string SaveToBase64() + { + using (var stream = new MemoryStream()) + { + SaveToStream(stream); + stream.Position = 0; + return Convert.ToBase64String(stream.ToArray()); + } + } + public static JSONNode Deserialize(BinaryReader aReader) + { + JSONBinaryTag type = (JSONBinaryTag)aReader.ReadByte(); + switch(type) + { + case JSONBinaryTag.Array: + { + int count = aReader.ReadInt32(); + JSONArray tmp = new JSONArray(); + for(int i = 0; i < count; i++) + tmp.Add(Deserialize(aReader)); + return tmp; + } + case JSONBinaryTag.Class: + { + int count = aReader.ReadInt32(); + JSONClass tmp = new JSONClass(); + for(int i = 0; i < count; i++) + { + string key = aReader.ReadString(); + var val = Deserialize(aReader); + tmp.Add(key, val); + } + return tmp; + } + case JSONBinaryTag.Value: + { + return new JSONData(aReader.ReadString()); + } + case JSONBinaryTag.IntValue: + { + return new JSONData(aReader.ReadInt32()); + } + case JSONBinaryTag.DoubleValue: + { + return new JSONData(aReader.ReadDouble()); + } + case JSONBinaryTag.BoolValue: + { + return new JSONData(aReader.ReadBoolean()); + } + case JSONBinaryTag.FloatValue: + { + return new JSONData(aReader.ReadSingle()); + } + + default: + { + throw new Exception("Error deserializing JSON. Unknown tag: " + type); + } + } + } + + #if USE_SharpZipLib + public static JSONNode LoadFromCompressedStream(System.IO.Stream aData) + { + var zin = new ICSharpCode.SharpZipLib.BZip2.BZip2InputStream(aData); + return LoadFromStream(zin); + } + public static JSONNode LoadFromCompressedFile(string aFileName) + { + #if USE_FileIO + using(var F = System.IO.File.OpenRead(aFileName)) + { + return LoadFromCompressedStream(F); + } + #else + throw new Exception("Can't use File IO stuff in webplayer"); + #endif + } + public static JSONNode LoadFromCompressedBase64(string aBase64) + { + var tmp = System.Convert.FromBase64String(aBase64); + var stream = new System.IO.MemoryStream(tmp); + stream.Position = 0; + return LoadFromCompressedStream(stream); + } + #else + public static JSONNode LoadFromCompressedFile(string aFileName) + { + throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); + } + public static JSONNode LoadFromCompressedStream(Stream aData) + { + throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); + } + public static JSONNode LoadFromCompressedBase64(string aBase64) + { + throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); + } + #endif + + public static JSONNode LoadFromStream(Stream aData) + { + using(var R = new BinaryReader(aData)) + { + return Deserialize(R); + } + } + public static JSONNode LoadFromFile(string aFileName) + { + #if USE_FileIO + using(var F = File.OpenRead(aFileName)) + { + return LoadFromStream(F); + } + #else + throw new Exception("Can't use File IO stuff in webplayer"); + #endif + } + public static JSONNode LoadFromBase64(string aBase64) + { + var tmp = Convert.FromBase64String(aBase64); + var stream = new MemoryStream(tmp); + stream.Position = 0; + return LoadFromStream(stream); + } + } // End of JSONNode + + public class JSONArray : JSONNode, IEnumerable + { + private List m_List = new List(); + public override JSONNode this[int aIndex] + { + get + { + if (aIndex<0 || aIndex >= m_List.Count) + return new JSONLazyCreator(this); + return m_List[aIndex]; + } + set + { + if (aIndex<0 || aIndex >= m_List.Count) + m_List.Add(value); + else + m_List[aIndex] = value; + } + } + public override JSONNode this[string aKey] + { + get{ return new JSONLazyCreator(this);} + set{ m_List.Add(value); } + } + public override int Count + { + get { return m_List.Count; } + } + public override void Add(string aKey, JSONNode aItem) + { + m_List.Add(aItem); + } + public override JSONNode Remove(int aIndex) + { + if (aIndex < 0 || aIndex >= m_List.Count) + return null; + JSONNode tmp = m_List[aIndex]; + m_List.RemoveAt(aIndex); + return tmp; + } + public override JSONNode Remove(JSONNode aNode) + { + m_List.Remove(aNode); + return aNode; + } + public override IEnumerable Childs + { + get + { + foreach(JSONNode N in m_List) + yield return N; + } + } + public IEnumerator GetEnumerator() + { + foreach(JSONNode N in m_List) + yield return N; + } + public override string ToString() + { + string result = "[ "; + foreach (JSONNode N in m_List) + { + if (result.Length > 2) + result += ", "; + result += N.ToString(); + } + result += " ]"; + return result; + } + public override string ToString(string aPrefix) + { + string result = "[ "; + foreach (JSONNode N in m_List) + { + if (result.Length > 3) + result += ", "; + result += "\n" + aPrefix + " "; + result += N.ToString(aPrefix+" "); + } + result += "\n" + aPrefix + "]"; + return result; + } + public override void Serialize (BinaryWriter aWriter) + { + aWriter.Write((byte)JSONBinaryTag.Array); + aWriter.Write(m_List.Count); + for(int i = 0; i < m_List.Count; i++) + { + m_List[i].Serialize(aWriter); + } + } + } // End of JSONArray + + public class JSONClass : JSONNode, IEnumerable + { + private Dictionary m_Dict = new Dictionary(StringComparer.Ordinal); + public override JSONNode this[string aKey] + { + get + { + if (m_Dict.ContainsKey(aKey)) + return m_Dict[aKey]; + return new JSONLazyCreator(this, aKey); + } + set + { + if (m_Dict.ContainsKey(aKey)) + m_Dict[aKey] = value; + else + m_Dict.Add(aKey,value); + } + } + public override JSONNode this[int aIndex] + { + get + { + if (aIndex < 0 || aIndex >= m_Dict.Count) + return null; + +#if DISABLE_LINQ_EXT + foreach (var kvp in m_Dict) + { + if (aIndex==0) + return kvp.Value; + aIndex--; + } + return null; +#else + return m_Dict.ElementAt(aIndex).Value; +#endif + } + set + { + if (aIndex < 0 || aIndex >= m_Dict.Count) + return; +#if DISABLE_LINQ_EXT + string[] keys = new string[m_Dict.Keys.Count]; + m_Dict.Keys.CopyTo(keys,0); + string key = keys[aIndex]; +#else + string key = m_Dict.ElementAt(aIndex).Key; +#endif + m_Dict[key] = value; + } + } + public override int Count + { + get { return m_Dict.Count; } + } + + + public override void Add(string aKey, JSONNode aItem) + { + if (!string.IsNullOrEmpty(aKey)) + { + if (m_Dict.ContainsKey(aKey)) + m_Dict[aKey] = aItem; + else + m_Dict.Add(aKey, aItem); + } + else + m_Dict.Add(Guid.NewGuid().ToString(), aItem); + } + + public override JSONNode Remove(string aKey) + { + if (!m_Dict.ContainsKey(aKey)) + return null; + JSONNode tmp = m_Dict[aKey]; + m_Dict.Remove(aKey); + return tmp; + } + public override JSONNode Remove(int aIndex) + { + if (aIndex < 0 || aIndex >= m_Dict.Count) + return null; + +#if DISABLE_LINQ_EXT + string[] keys = new string[m_Dict.Keys.Count]; + m_Dict.Keys.CopyTo(keys,0); + string key = keys[aIndex]; + var value = m_Dict[key]; + m_Dict.Remove(key); + return value; +#else + var item = m_Dict.ElementAt(aIndex); + m_Dict.Remove(item.Key); + return item.Value; +#endif + } + public override JSONNode Remove(JSONNode aNode) + { + try + { +#if DISABLE_LINQ_EXT + foreach (var kvp in m_Dict) + if (kvp.Value == aNode) + { + m_Dict.Remove(kvp.Key); + break; + } + return aNode; +#else + var item = m_Dict.Where(k => k.Value == aNode).First(); + m_Dict.Remove(item.Key); + return aNode; +#endif + } + catch + { + return null; + } + } + + public override IEnumerable Childs + { + get + { + foreach(KeyValuePair N in m_Dict) + yield return N.Value; + } + } + + public IEnumerator GetEnumerator() + { + foreach(KeyValuePair N in m_Dict) + yield return N; + } + public override string ToString() + { + string result = "{"; + foreach (KeyValuePair N in m_Dict) + { + if (result.Length > 2) + result += ", "; + result += "\"" + Escape(N.Key) + "\":" + N.Value; + } + result += "}"; + return result; + } + public override string ToString(string aPrefix) + { + string result = "{ "; + foreach (KeyValuePair N in m_Dict) + { + if (result.Length > 3) + result += ", "; + result += "\n" + aPrefix + " "; + result += "\"" + Escape(N.Key) + "\" : " + N.Value.ToString(aPrefix+" "); + } + result += "\n" + aPrefix + "}"; + return result; + } + public override void Serialize (BinaryWriter aWriter) + { + aWriter.Write((byte)JSONBinaryTag.Class); + aWriter.Write(m_Dict.Count); + foreach(string K in m_Dict.Keys) + { + aWriter.Write(K); + m_Dict[K].Serialize(aWriter); + } + } + } // End of JSONClass + + public class JSONData : JSONNode + { + private string m_Data; + public override string Value + { + get { return m_Data; } + set { m_Data = value; } + } + public JSONData(string aData) + { + m_Data = aData; + } + public JSONData(float aData) + { + AsFloat = aData; + } + public JSONData(double aData) + { + AsDouble = aData; + } + public JSONData(bool aData) + { + AsBool = aData; + } + public JSONData(int aData) + { + AsInt = aData; + } + + public override string ToString() + { + return "\"" + Escape(m_Data) + "\""; + } + public override string ToString(string aPrefix) + { + return "\"" + Escape(m_Data) + "\""; + } + public override void Serialize (BinaryWriter aWriter) + { + var tmp = new JSONData(""); + + tmp.AsInt = AsInt; + if (tmp.m_Data == m_Data) + { + aWriter.Write((byte)JSONBinaryTag.IntValue); + aWriter.Write(AsInt); + return; + } + tmp.AsFloat = AsFloat; + if (tmp.m_Data == m_Data) + { + aWriter.Write((byte)JSONBinaryTag.FloatValue); + aWriter.Write(AsFloat); + return; + } + tmp.AsDouble = AsDouble; + if (tmp.m_Data == m_Data) + { + aWriter.Write((byte)JSONBinaryTag.DoubleValue); + aWriter.Write(AsDouble); + return; + } + + tmp.AsBool = AsBool; + if (tmp.m_Data == m_Data) + { + aWriter.Write((byte)JSONBinaryTag.BoolValue); + aWriter.Write(AsBool); + return; + } + aWriter.Write((byte)JSONBinaryTag.Value); + aWriter.Write(m_Data); + } + } // End of JSONData + + internal class JSONLazyCreator : JSONNode + { + private JSONNode m_Node; + private string m_Key; + + public JSONLazyCreator(JSONNode aNode) + { + m_Node = aNode; + m_Key = null; + } + public JSONLazyCreator(JSONNode aNode, string aKey) + { + m_Node = aNode; + m_Key = aKey; + } + + private void Set(JSONNode aVal) + { + if (m_Key == null) + { + m_Node.Add(aVal); + } + else + { + m_Node.Add(m_Key, aVal); + } + m_Node = null; // Be GC friendly. + } + + public override JSONNode this[int aIndex] + { + get + { + return new JSONLazyCreator(this); + } + set + { + var tmp = new JSONArray(); + tmp.Add(value); + Set(tmp); + } + } + + public override JSONNode this[string aKey] + { + get + { + return new JSONLazyCreator(this, aKey); + } + set + { + var tmp = new JSONClass(); + tmp.Add(aKey, value); + Set(tmp); + } + } + public override void Add (JSONNode aItem) + { + var tmp = new JSONArray(); + tmp.Add(aItem); + Set(tmp); + } + public override void Add (string aKey, JSONNode aItem) + { + var tmp = new JSONClass(); + tmp.Add(aKey, aItem); + Set(tmp); + } + public static bool operator ==(JSONLazyCreator a, object b) + { + if (b == null) + return true; + return ReferenceEquals(a,b); + } + + public static bool operator !=(JSONLazyCreator a, object b) + { + return !(a == b); + } + public override bool Equals (object obj) + { + if (obj == null) + return true; + return ReferenceEquals(this, obj); + } + public override int GetHashCode () + { + return base.GetHashCode(); + } + + public override string ToString() + { + return ""; + } + public override string ToString(string aPrefix) + { + return ""; + } + + public override int AsInt + { + get + { + JSONData tmp = new JSONData(0); + Set(tmp); + return 0; + } + set + { + JSONData tmp = new JSONData(value); + Set(tmp); + } + } + public override float AsFloat + { + get + { + JSONData tmp = new JSONData(0.0f); + Set(tmp); + return 0.0f; + } + set + { + JSONData tmp = new JSONData(value); + Set(tmp); + } + } + public override double AsDouble + { + get + { + JSONData tmp = new JSONData(0.0); + Set(tmp); + return 0.0; + } + set + { + JSONData tmp = new JSONData(value); + Set(tmp); + } + } + public override bool AsBool + { + get + { + JSONData tmp = new JSONData(false); + Set(tmp); + return false; + } + set + { + JSONData tmp = new JSONData(value); + Set(tmp); + } + } + public override JSONArray AsArray + { + get + { + JSONArray tmp = new JSONArray(); + Set(tmp); + return tmp; + } + } + public override JSONClass AsObject + { + get + { + JSONClass tmp = new JSONClass(); + Set(tmp); + return tmp; + } + } + } // End of JSONLazyCreator + + public static class JSON + { + public static JSONNode Parse(string aJSON) + { + return JSONNode.Parse(aJSON); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/SimpleJSON.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/SimpleJSON.cs.meta new file mode 100644 index 00000000..a86ec175 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/SimpleJSON.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: c6bd173cd8524b04daa3dabce666b456 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob.cs new file mode 100644 index 00000000..786e0e2f --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using UnityEngine.Networking; + +namespace TEngine.Localization +{ + using TranslationDictionary = Dictionary; + + + public class TranslationJob : IDisposable + { + public eJobState mJobState = eJobState.Running; + + public enum eJobState { Running, Succeeded, Failed } + + public virtual eJobState GetState() { return mJobState; } + + public virtual void Dispose() { } + + } + + public class TranslationJob_WWW : TranslationJob + { + public UnityWebRequest www; + + public override void Dispose() + { + if (www!=null) + www.Dispose(); + www = null; + } + + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob.cs.meta new file mode 100644 index 00000000..3dd1594d --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 7dae211a5bb44db46a568fb70fca3296 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_GET.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_GET.cs new file mode 100644 index 00000000..db294862 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_GET.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.Text; +using UnityEngine.Networking; + +namespace TEngine.Localization +{ + using TranslationDictionary = Dictionary; + + public class TranslationJob_GET : TranslationJob_WWW + { + TranslationDictionary _requests; + GoogleTranslation.fnOnTranslationReady _OnTranslationReady; + List mQueries; + public string mErrorMessage; + + public TranslationJob_GET(TranslationDictionary requests, GoogleTranslation.fnOnTranslationReady OnTranslationReady) + { + _requests = requests; + _OnTranslationReady = OnTranslationReady; + + mQueries = GoogleTranslation.ConvertTranslationRequest(requests, true); + + GetState(); + } + + void ExecuteNextQuery() + { + if (mQueries.Count == 0) + { + mJobState = eJobState.Succeeded; + return; + } + + int lastQuery = mQueries.Count - 1; + var query = mQueries[lastQuery]; + mQueries.RemoveAt(lastQuery); + + string url = $"{LocalizationManager.GetWebServiceURL()}?action=Translate&list={query}"; + www = UnityWebRequest.Get(url); + I2Utils.SendWebRequest(www); + } + + public override eJobState GetState() + { + if (www != null && www.isDone) + { + ProcessResult(www.downloadHandler.data, www.error); + www.Dispose(); + www = null; + } + + if (www==null) + { + ExecuteNextQuery(); + } + + return mJobState; + } + + public void ProcessResult(byte[] bytes, string errorMsg) + { + if (string.IsNullOrEmpty(errorMsg)) + { + var wwwText = Encoding.UTF8.GetString(bytes, 0, bytes.Length); //www.text + errorMsg = GoogleTranslation.ParseTranslationResult(wwwText, _requests); + + if (string.IsNullOrEmpty(errorMsg)) + { + if (_OnTranslationReady!=null) + _OnTranslationReady(_requests, null); + return; + } + } + + mJobState = eJobState.Failed; + mErrorMessage = errorMsg; + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_GET.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_GET.cs.meta new file mode 100644 index 00000000..7835a839 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_GET.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 13df42f7287eccc4399bc757d577030e +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_Main.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_Main.cs new file mode 100644 index 00000000..9f4d6f96 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_Main.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; + +namespace TEngine.Localization +{ + using TranslationDictionary = Dictionary; + + public class TranslationJob_Main : TranslationJob + { + TranslationJob_WEB mWeb; + TranslationJob_POST mPost; + TranslationJob_GET mGet; + + TranslationDictionary _requests; + GoogleTranslation.fnOnTranslationReady _OnTranslationReady; + public string mErrorMessage; + + public TranslationJob_Main(TranslationDictionary requests, GoogleTranslation.fnOnTranslationReady OnTranslationReady) + { + _requests = requests; + _OnTranslationReady = OnTranslationReady; + + //mWeb = new TranslationJob_WEB(requests, OnTranslationReady); + mPost = new TranslationJob_POST(requests, OnTranslationReady); + } + + public override eJobState GetState() + { + if (mWeb != null) + { + var state = mWeb.GetState(); + switch (state) + { + case eJobState.Running: return eJobState.Running; + case eJobState.Succeeded: + { + mJobState = eJobState.Succeeded; + break; + } + case eJobState.Failed: + { + mWeb.Dispose(); + mWeb = null; + mPost = new TranslationJob_POST(_requests, _OnTranslationReady); + break; + } + } + } + if (mPost != null) + { + var state = mPost.GetState(); + switch (state) + { + case eJobState.Running: return eJobState.Running; + case eJobState.Succeeded: + { + mJobState = eJobState.Succeeded; + break; + } + case eJobState.Failed: + { + mPost.Dispose(); + mPost = null; + mGet = new TranslationJob_GET(_requests, _OnTranslationReady); + break; + } + } + } + if (mGet != null) + { + var state = mGet.GetState(); + switch (state) + { + case eJobState.Running: return eJobState.Running; + case eJobState.Succeeded: + { + mJobState = eJobState.Succeeded; + break; + } + case eJobState.Failed: + { + mErrorMessage = mGet.mErrorMessage; + if (_OnTranslationReady != null) + _OnTranslationReady(_requests, mErrorMessage); + mGet.Dispose(); + mGet = null; + break; + } + } + } + + return mJobState; + } + + public override void Dispose() + { + if (mPost != null) mPost.Dispose(); + if (mGet != null) mGet.Dispose(); + mPost = null; + mGet = null; + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_Main.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_Main.cs.meta new file mode 100644 index 00000000..43390b4a --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_Main.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: b6aed7ae05f9c254596b3728c5811bdb +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_POST.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_POST.cs new file mode 100644 index 00000000..4f111681 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_POST.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using UnityEngine.Networking; + +namespace TEngine.Localization +{ + using TranslationDictionary = Dictionary; + + public class TranslationJob_POST : TranslationJob_WWW + { + TranslationDictionary _requests; + GoogleTranslation.fnOnTranslationReady _OnTranslationReady; + + public TranslationJob_POST(TranslationDictionary requests, GoogleTranslation.fnOnTranslationReady OnTranslationReady) + { + _requests = requests; + _OnTranslationReady = OnTranslationReady; + + var data = GoogleTranslation.ConvertTranslationRequest(requests, false); + + WWWForm form = new WWWForm(); + form.AddField("action", "Translate"); + form.AddField("list", data[0]); + + www = UnityWebRequest.Post(LocalizationManager.GetWebServiceURL(), form); + I2Utils.SendWebRequest(www); + } + + public override eJobState GetState() + { + if (www != null && www.isDone) + { + ProcessResult(www.downloadHandler.data, www.error); + www.Dispose(); + www = null; + } + + return mJobState; + } + + public void ProcessResult(byte[] bytes, string errorMsg) + { + if (!string.IsNullOrEmpty(errorMsg)) + { + // check for + //if (errorMsg.Contains("rewind")) // "necessary data rewind wasn't possible" + mJobState = eJobState.Failed; + } + else + { + var wwwText = Encoding.UTF8.GetString(bytes, 0, bytes.Length); //www.text + errorMsg = GoogleTranslation.ParseTranslationResult(wwwText, _requests); + if (_OnTranslationReady!=null) + _OnTranslationReady(_requests, errorMsg); + mJobState = eJobState.Succeeded; + } + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_POST.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_POST.cs.meta new file mode 100644 index 00000000..abc2d46f --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_POST.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 3450e33294f339348ae4e52731974230 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_WEB.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_WEB.cs new file mode 100644 index 00000000..93f1448c --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_WEB.cs @@ -0,0 +1,169 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; +using UnityEngine; +using UnityEngine.Networking; + +namespace TEngine.Localization +{ + using TranslationDictionary = Dictionary; + + public class TranslationJob_WEB : TranslationJob_WWW + { + TranslationDictionary _requests; + GoogleTranslation.fnOnTranslationReady _OnTranslationReady; + public string mErrorMessage; + + string mCurrentBatch_ToLanguageCode; + string mCurrentBatch_FromLanguageCode; + List mCurrentBatch_Text; + + List> mQueries; + + public TranslationJob_WEB(TranslationDictionary requests, GoogleTranslation.fnOnTranslationReady OnTranslationReady) + { + _requests = requests; + _OnTranslationReady = OnTranslationReady; + + FindAllQueries(); + ExecuteNextBatch(); + } + + void FindAllQueries() + { + mQueries = new List>(); + foreach (var kvp in _requests) + { + foreach (var langCode in kvp.Value.TargetLanguagesCode) + { + mQueries.Add(new KeyValuePair(kvp.Value.OrigText, kvp.Value.LanguageCode+":"+langCode)); + } + } + + mQueries.Sort((a, b) => a.Value.CompareTo(b.Value)); + } + + void ExecuteNextBatch() + { + if (mQueries.Count==0) + { + mJobState = eJobState.Succeeded; + return; + } + mCurrentBatch_Text = new List(); + + string lastLangCode = null; + int maxLength = 200; + + var sb = new StringBuilder(); + int i; + for (i=0; i maxLength) + break; + } + mQueries.RemoveRange(0, i); + + var fromtoLang = lastLangCode.Split(':'); + mCurrentBatch_FromLanguageCode = fromtoLang[0]; + mCurrentBatch_ToLanguageCode = fromtoLang[1]; + + string url = string.Format ("http://www.google.com/translate_t?hl=en&vi=c&ie=UTF8&oe=UTF8&submit=Translate&langpair={0}|{1}&text={2}", mCurrentBatch_FromLanguageCode, mCurrentBatch_ToLanguageCode, Uri.EscapeUriString( sb.ToString() )); + Debug.Log(url); + + www = UnityWebRequest.Get(url); + I2Utils.SendWebRequest(www); + } + + public override eJobState GetState() + { + if (www != null && www.isDone) + { + ProcessResult(www.downloadHandler.data, www.error); + www.Dispose(); + www = null; + } + + if (www == null) + { + ExecuteNextBatch(); + } + + return mJobState; + } + + public void ProcessResult(byte[] bytes, string errorMsg) + { + if (string.IsNullOrEmpty(errorMsg)) + { + var wwwText = Encoding.UTF8.GetString(bytes, 0, bytes.Length); //www.text + var result = ParseTranslationResult(wwwText, "aab"); + //errorMsg = GoogleTranslation.ParseTranslationResult(wwwText, _requests); + Debug.Log(result); + + if (string.IsNullOrEmpty(errorMsg)) + { + if (_OnTranslationReady != null) + _OnTranslationReady(_requests, null); + return; + } + } + + mJobState = eJobState.Failed; + mErrorMessage = errorMsg; + } + + string ParseTranslationResult( string html, string OriginalText ) + { + try + { + // This is a Hack for reading Google Translation while Google doens't change their response format + int iStart = html.IndexOf("TRANSLATED_TEXT='", StringComparison.Ordinal) + "TRANSLATED_TEXT='".Length; + int iEnd = html.IndexOf("';var", iStart, StringComparison.Ordinal); + + string Translation = html.Substring( iStart, iEnd-iStart); + + // Convert to normalized HTML + Translation = Regex.Replace(Translation, @"\\x([a-fA-F0-9]{2})", + match => char.ConvertFromUtf32(int.Parse(match.Groups[1].Value, NumberStyles.HexNumber))); + + // Convert ASCII Characters + Translation = Regex.Replace(Translation, @"&#(\d+);", + match => char.ConvertFromUtf32(int.Parse(match.Groups[1].Value))); + + Translation = Translation.Replace("
", "\n"); + + if (OriginalText.ToUpper()==OriginalText) + Translation = Translation.ToUpper(); + else + if (GoogleTranslation.UppercaseFirst(OriginalText)==OriginalText) + Translation = GoogleTranslation.UppercaseFirst(Translation); + else + if (GoogleTranslation.TitleCase(OriginalText)==OriginalText) + Translation = GoogleTranslation.TitleCase(Translation); + + return Translation; + } + catch (Exception ex) + { + Debug.LogError(ex.Message); + return string.Empty; + } + } + } + + } \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_WEB.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_WEB.cs.meta new file mode 100644 index 00000000..408e6adb --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Google/TranslationJob_WEB.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 3b72399bd2ff39042b1320bb943e0a20 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageData.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageData.cs new file mode 100644 index 00000000..ae62a361 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageData.cs @@ -0,0 +1,43 @@ +using System; + +namespace TEngine.Localization +{ + public enum eLanguageDataFlags + { + DISABLED = 1, + KEEP_LOADED = 2, + NOT_LOADED = 4 + } + [Serializable] + public class LanguageData + { + public string Name; + public string Code; + public byte Flags; // eLanguageDataFlags + + [NonSerialized] + public bool Compressed = false; // This will be used in the next version for only loading used Languages + + public bool IsEnabled () { return (Flags & (int)eLanguageDataFlags.DISABLED) == 0; } + + public void SetEnabled( bool bEnabled ) + { + if (bEnabled) Flags = (byte)(Flags & ~(int)eLanguageDataFlags.DISABLED); + else Flags = (byte)(Flags | (int)eLanguageDataFlags.DISABLED); + } + + public bool IsLoaded () { return (Flags & (int)eLanguageDataFlags.NOT_LOADED) == 0; } + public bool CanBeUnloaded () { return (Flags & (int)eLanguageDataFlags.KEEP_LOADED) == 0; } + + public void SetLoaded ( bool loaded ) + { + if (loaded) Flags = (byte)(Flags & ~(int)eLanguageDataFlags.NOT_LOADED); + else Flags = (byte)(Flags | (int)eLanguageDataFlags.NOT_LOADED); + } + public void SetCanBeUnLoaded(bool allowUnloading) + { + if (allowUnloading) Flags = (byte)(Flags & ~(int)eLanguageDataFlags.KEEP_LOADED); + else Flags = (byte)(Flags | (int)eLanguageDataFlags.KEEP_LOADED); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageData.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageData.cs.meta new file mode 100644 index 00000000..6ce04567 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageData.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: cde54ba5e482bec4c88b52e618b81cc9 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource.meta new file mode 100644 index 00000000..f6447daf --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 75c7654f6d20207418b4cbce1b29906d +folderAsset: yes +timeCreated: 1518833279 +licenseType: Store +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSource.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSource.cs new file mode 100644 index 00000000..f478c481 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSource.cs @@ -0,0 +1,179 @@ +using System.Collections.Generic; +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + [AddComponentMenu("I2/Localization/Source")] + [ExecuteInEditMode] + public class LanguageSource : MonoBehaviour, ISerializationCallbackReceiver, ILanguageSource + { + public LanguageSourceData SourceData + { + get { return mSource; } + set { mSource = value; } + } + public LanguageSourceData mSource = new LanguageSourceData(); + + // Because of Unity2018.3 change in Prefabs, now all the source variables are moved into LanguageSourceData + // But to avoid loosing previously serialized data, these vars are copied into mSource.XXXX when deserializing) + // These are going to be removed once everyone port their projects to the new I2L version. + #region Legacy Variables + + // TODO: also copy public string name; and owner + + public int version; + public bool NeverDestroy; // Keep between scenes (will call DontDestroyOnLoad ) + + public bool UserAgreesToHaveItOnTheScene; + public bool UserAgreesToHaveItInsideThePluginsFolder; + public bool GoogleLiveSyncIsUptoDate = true; + + public List Assets = new List(); // References to Fonts, Atlasses and other objects the localization may need + + public string Google_WebServiceURL; + public string Google_SpreadsheetKey; + public string Google_SpreadsheetName; + public string Google_LastUpdatedVersion; + + + public LanguageSourceData.eGoogleUpdateFrequency GoogleUpdateFrequency = LanguageSourceData.eGoogleUpdateFrequency.Weekly; + + public float GoogleUpdateDelay = 5; // How many second to delay downloading data from google (to avoid lag on the startup) + + public delegate void fnOnSourceUpdated(LanguageSourceData source, bool ReceivedNewData, string errorMsg); + public event fnOnSourceUpdated Event_OnSourceUpdateFromGoogle; + + public List mLanguages = new List(); + + public bool IgnoreDeviceLanguage; // If false, it will use the Device's language as the initial Language, otherwise it will use the first language in the source. + + public LanguageSourceData.eAllowUnloadLanguages _AllowUnloadingLanguages = LanguageSourceData.eAllowUnloadLanguages.Never; + + public List mTerms = new List(); + + public bool CaseInsensitiveTerms; + + public LanguageSourceData.MissingTranslationAction OnMissingTranslation = LanguageSourceData.MissingTranslationAction.Fallback; + + public string mTerm_AppName; + + #endregion + + #region EditorVariables + #if UNITY_EDITOR + + public string Spreadsheet_LocalFileName; + public string Spreadsheet_LocalCSVSeparator = ","; + public string Spreadsheet_LocalCSVEncoding = "utf-8"; + public bool Spreadsheet_SpecializationAsRows = true; + public bool Spreadsheet_SortRows = true; + + public string Google_Password = "change_this"; + public LanguageSourceData.eGoogleUpdateFrequency GoogleInEditorCheckFrequency = LanguageSourceData.eGoogleUpdateFrequency.Daily; +#endif + #endregion + + void Awake() + { + #if UNITY_EDITOR + if (BuildPipeline.isBuildingPlayer) + return; + #endif + // NeverDestroy = false; + + // if (NeverDestroy) + //{ + // if (mSource.ManagerHasASimilarSource()) + // { + // Object.Destroy (this); + // return; + // } + // else + // { + // if (Application.isPlaying) + // DontDestroyOnLoad (gameObject); + // } + //} + mSource.owner = this; + mSource.Awake(); + } + + private void OnDestroy() + { + NeverDestroy = false; + + if (!NeverDestroy) + { + mSource.OnDestroy(); + } + } + + public string GetSourceName() + { + string s = gameObject.name; + Transform tr = transform.parent; + while (tr) + { + s = string.Concat(tr.name, "_", s); + tr = tr.parent; + } + return s; + } + + public void OnBeforeSerialize() + { + version = 1; + } + + public void OnAfterDeserialize() + { + if (version==0 || mSource==null) + { + mSource = new LanguageSourceData(); + mSource.owner = this; + mSource.UserAgreesToHaveItOnTheScene = UserAgreesToHaveItOnTheScene; + mSource.UserAgreesToHaveItInsideThePluginsFolder = UserAgreesToHaveItInsideThePluginsFolder; + mSource.IgnoreDeviceLanguage = IgnoreDeviceLanguage; + mSource._AllowUnloadingLanguages = _AllowUnloadingLanguages; + mSource.CaseInsensitiveTerms = CaseInsensitiveTerms; + mSource.OnMissingTranslation = OnMissingTranslation; + mSource.mTerm_AppName = mTerm_AppName; + + mSource.GoogleLiveSyncIsUptoDate = GoogleLiveSyncIsUptoDate; + mSource.Google_WebServiceURL = Google_WebServiceURL; + mSource.Google_SpreadsheetKey = Google_SpreadsheetKey; + mSource.Google_SpreadsheetName = Google_SpreadsheetName; + mSource.Google_LastUpdatedVersion = Google_LastUpdatedVersion; + mSource.GoogleUpdateFrequency = GoogleUpdateFrequency; + mSource.GoogleUpdateDelay = GoogleUpdateDelay; + + mSource.Event_OnSourceUpdateFromGoogle += Event_OnSourceUpdateFromGoogle; + + if (mLanguages != null && mLanguages.Count>0) + { + mSource.mLanguages.Clear(); + mSource.mLanguages.AddRange(mLanguages); + mLanguages.Clear(); + } + if (Assets != null && Assets.Count > 0) + { + mSource.Assets.Clear(); + mSource.Assets.AddRange(Assets); + Assets.Clear(); + } + if (mTerms != null && mTerms.Count>0) + { + mSource.mTerms.Clear(); + for (int i=0; i mTerms = new List(); + + public bool CaseInsensitiveTerms; + + //This is used to overcome the issue with Unity not serializing Dictionaries + [NonSerialized] public Dictionary mDictionary = new Dictionary(StringComparer.Ordinal); + + public enum MissingTranslationAction { Empty, Fallback, ShowWarning, ShowTerm } + public MissingTranslationAction OnMissingTranslation = MissingTranslationAction.Fallback; + + public string mTerm_AppName; + + #endregion + + #region Variables : Languages + + public List mLanguages = new List(); + + public bool IgnoreDeviceLanguage; // If false, it will use the Device's language as the initial Language, otherwise it will use the first language in the source. + + public enum eAllowUnloadLanguages { Never, OnlyInDevice, EditorAndDevice } + public eAllowUnloadLanguages _AllowUnloadingLanguages = eAllowUnloadLanguages.Never; + + #endregion + + #region Variables : Google + + public string Google_WebServiceURL; + public string Google_SpreadsheetKey; + public string Google_SpreadsheetName; + public string Google_LastUpdatedVersion; + +#if UNITY_EDITOR + public string Google_Password = "change_this"; +#endif + + public enum eGoogleUpdateFrequency { Always, Never, Daily, Weekly, Monthly, OnlyOnce, EveryOtherDay } + public eGoogleUpdateFrequency GoogleUpdateFrequency = eGoogleUpdateFrequency.Weekly; + public eGoogleUpdateFrequency GoogleInEditorCheckFrequency = eGoogleUpdateFrequency.Daily; + + // When Manual, the user has to call LocalizationManager.ApplyDownloadedDataFromGoogle() during a loading screen or similar + public enum eGoogleUpdateSynchronization { Manual, OnSceneLoaded, AsSoonAsDownloaded } + public eGoogleUpdateSynchronization GoogleUpdateSynchronization = eGoogleUpdateSynchronization.OnSceneLoaded; + + public float GoogleUpdateDelay; // How many second to delay downloading data from google (to avoid lag on the startup) + + public event LanguageSource.fnOnSourceUpdated Event_OnSourceUpdateFromGoogle; // (LanguageSource, bool ReceivedNewData, string errorMsg) + + #endregion + + #region Variables : Assets + + public List Assets = new List(); // References to Fonts, Atlasses and other objects the localization may need + + //This is used to overcome the issue with Unity not serializing Dictionaries + [NonSerialized] public Dictionary mAssetDictionary = new Dictionary(StringComparer.Ordinal); + + #endregion + + #region EditorVariables +#if UNITY_EDITOR + + public string Spreadsheet_LocalFileName; + public string Spreadsheet_LocalCSVSeparator = ","; + public string Spreadsheet_LocalCSVEncoding = "utf-8"; + public bool Spreadsheet_SpecializationAsRows = true; + public bool Spreadsheet_SortRows = true; + +#endif + #endregion + + #region Language + + public void Awake() + { + LocalizationManager.AddSource (this); + UpdateDictionary(); + UpdateAssetDictionary(); + LocalizationManager.LocalizeAll(true); + } + + public void OnDestroy() + { + LocalizationManager.RemoveSource(this); + } + + + + public bool IsEqualTo( LanguageSourceData Source ) + { + if (Source.mLanguages.Count != mLanguages.Count) + return false; + + for (int i=0, imax=mLanguages.Count; i x == null); + mAssetDictionary = Assets.Distinct() + .GroupBy(o => o.name, System.StringComparer.Ordinal) + .ToDictionary(g => g.Key, g => g.First(), System.StringComparer.Ordinal); + } + + public Object FindAsset( string Name ) + { + if (Assets!=null) + { + if (mAssetDictionary==null || mAssetDictionary.Count!=Assets.Count) + { + UpdateAssetDictionary(); + } + Object obj; + if (mAssetDictionary.TryGetValue(Name, out obj)) + { + return obj; + } + //for (int i=0, imax=Assets.Length; i 0) + sb.Append("[i2t]"); + var term = mTerms[i]; + sb.Append(term.Term); + sb.Append("="); + + var translation = term.Languages[langIndex]; + if (OnMissingTranslation == MissingTranslationAction.Fallback && string.IsNullOrEmpty(translation)) + if (TryGetFallbackTranslation(term, out translation, langIndex, skipDisabled: true)) + { + sb.Append("[i2fb]"); + if (fillTermWithFallback) term.Languages[langIndex] = translation; + } + + if (!string.IsNullOrEmpty(translation)) + sb.Append(translation); + } + + return sb.ToString(); + } + + #endregion + + #region I2CSV format + + public string Export_I2CSV(string Category, char Separator = ',', bool specializationsAsRows = true, bool sortRows=true) + { + var Builder = new StringBuilder(); + + //--[ Header ]---------------------------------- + Builder.Append("Key[*]Type[*]Desc"); + foreach (var langData in mLanguages) + { + Builder.Append("[*]"); + if (!langData.IsEnabled()) + Builder.Append('$'); + Builder.Append(GoogleLanguages.GetCodedLanguage(langData.Name, langData.Code)); + } + + Builder.Append("[ln]"); + + if (sortRows) + { + mTerms.Sort((a, b) => string.CompareOrdinal(a.Term, b.Term)); + } + + var nLanguages = mLanguages.Count; + var firstLine = true; + foreach (var termData in mTerms) + { + string Term; + + if (string.IsNullOrEmpty(Category) || + Category == EmptyCategory && termData.Term.IndexOfAny(CategorySeparators) < 0) + Term = termData.Term; + else if (termData.Term.StartsWith(Category + @"/", StringComparison.Ordinal) && + Category != termData.Term) + Term = termData.Term.Substring(Category.Length + 1); + else + continue; // Term doesn't belong to this category + + + if (!firstLine) Builder.Append("[ln]"); + firstLine = false; + + if (!specializationsAsRows) + { + AppendI2Term(Builder, nLanguages, Term, termData, Separator, null); + } + else + { + var allSpecializations = termData.GetAllSpecializations(); + for (var i = 0; i < allSpecializations.Count; ++i) + { + if (i != 0) + Builder.Append("[ln]"); + var specialization = allSpecializations[i]; + AppendI2Term(Builder, nLanguages, Term, termData, Separator, specialization); + } + } + } + + return Builder.ToString(); + } + + private static void AppendI2Term(StringBuilder Builder, int nLanguages, string Term, TermData termData, + char Separator, string forceSpecialization) + { + //--[ Key ] -------------- + AppendI2Text(Builder, Term); + if (!string.IsNullOrEmpty(forceSpecialization) && forceSpecialization != "Any") + { + Builder.Append("["); + Builder.Append(forceSpecialization); + Builder.Append("]"); + } + + Builder.Append("[*]"); + + //--[ Type and Description ] -------------- + Builder.Append(termData.TermType.ToString()); + Builder.Append("[*]"); + Builder.Append(termData.Description); + + //--[ Languages ] -------------- + for (var i = 0; i < Mathf.Min(nLanguages, termData.Languages.Length); ++i) + { + Builder.Append("[*]"); + + var translation = termData.Languages[i]; + if (!string.IsNullOrEmpty(forceSpecialization)) + translation = termData.GetTranslation(i, forceSpecialization); + + //bool isAutoTranslated = ((termData.Flags[i]&FlagBitMask)>0); + + /*if (translation == null) + translation = string.Empty; + else + if (translation == "") + translation = "-";*/ + //if (isAutoTranslated) Builder.Append("[i2auto]"); + AppendI2Text(Builder, translation); + } + } + + private static void AppendI2Text(StringBuilder Builder, string text) + { + if (string.IsNullOrEmpty(text)) + return; + + if (text.StartsWith("\'", StringComparison.Ordinal) || text.StartsWith("=", StringComparison.Ordinal)) + Builder.Append('\''); + Builder.Append(text); + } + + #endregion + + #region CSV format + + public string Export_CSV(string Category, char Separator = ',', bool specializationsAsRows = true, bool sortRows=true) + { + var Builder = new StringBuilder(); + + var nLanguages = mLanguages.Count; + Builder.AppendFormat("Key{0}Type{0}Desc", Separator); + + foreach (var langData in mLanguages) + { + Builder.Append(Separator); + if (!langData.IsEnabled()) + Builder.Append('$'); + AppendString(Builder, GoogleLanguages.GetCodedLanguage(langData.Name, langData.Code), Separator); + } + + Builder.Append("\n"); + + + if (sortRows) + { + mTerms.Sort((a, b) => string.CompareOrdinal(a.Term, b.Term)); + } + + foreach (var termData in mTerms) + { + string Term; + + if (string.IsNullOrEmpty(Category) || + Category == EmptyCategory && termData.Term.IndexOfAny(CategorySeparators) < 0) + Term = termData.Term; + else if (termData.Term.StartsWith(Category + @"/", StringComparison.Ordinal) && + Category != termData.Term) + Term = termData.Term.Substring(Category.Length + 1); + else + continue; // Term doesn't belong to this category + + if (specializationsAsRows) + foreach (var specialization in termData.GetAllSpecializations()) + AppendTerm(Builder, nLanguages, Term, termData, specialization, Separator); + else + AppendTerm(Builder, nLanguages, Term, termData, null, Separator); + } + + return Builder.ToString(); + } + + private static void AppendTerm(StringBuilder Builder, int nLanguages, string Term, TermData termData, + string specialization, char Separator) + { + //--[ Key ] -------------- + AppendString(Builder, Term, Separator); + + if (!string.IsNullOrEmpty(specialization) && specialization != "Any") + Builder.AppendFormat("[{0}]", specialization); + + //--[ Type and Description ] -------------- + Builder.Append(Separator); + Builder.Append(termData.TermType.ToString()); + Builder.Append(Separator); + AppendString(Builder, termData.Description, Separator); + + //--[ Languages ] -------------- + for (var i = 0; i < Mathf.Min(nLanguages, termData.Languages.Length); ++i) + { + Builder.Append(Separator); + + var translation = termData.Languages[i]; + if (!string.IsNullOrEmpty(specialization)) + translation = termData.GetTranslation(i, specialization); + + //bool isAutoTranslated = ((termData.Flags[i]&FlagBitMask)>0); + + //if (string.IsNullOrEmpty(s)) + // s = "-"; + + AppendTranslation(Builder, translation, Separator, /*isAutoTranslated ? "[i2auto]" : */null); + } + + Builder.Append("\n"); + } + + + private static void AppendString(StringBuilder Builder, string Text, char Separator) + { + if (string.IsNullOrEmpty(Text)) + return; + Text = Text.Replace("\\n", "\n"); + if (Text.IndexOfAny((Separator + "\n\"").ToCharArray()) >= 0) + { + Text = Text.Replace("\"", "\"\""); + Builder.AppendFormat("\"{0}\"", Text); + } + else + { + Builder.Append(Text); + } + } + + private static void AppendTranslation(StringBuilder Builder, string Text, char Separator, string tags) + { + if (string.IsNullOrEmpty(Text)) + return; + Text = Text.Replace("\\n", "\n"); + if (Text.IndexOfAny((Separator + "\n\"").ToCharArray()) >= 0) + { + Text = Text.Replace("\"", "\"\""); + Builder.AppendFormat("\"{0}{1}\"", tags, Text); + } + else + { + Builder.Append(tags); + Builder.Append(Text); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Export_CSV.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Export_CSV.cs.meta new file mode 100644 index 00000000..39b942db --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Export_CSV.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 7a375153c79873c469c79a5b4f9027f3 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Export_Google.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Export_Google.cs new file mode 100644 index 00000000..9496ffe1 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Export_Google.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.Text; +using UnityEngine; +using UnityEngine.Networking; + +namespace TEngine.Localization +{ + public enum eSpreadsheetUpdateMode { None, Replace, Merge, AddNewTerms } + + public partial class LanguageSourceData + { + public UnityWebRequest Export_Google_CreateWWWcall( eSpreadsheetUpdateMode UpdateMode = eSpreadsheetUpdateMode.Replace ) + { + #if UNITY_WEBPLAYER + Debug.Log ("Contacting google translation is not yet supported on WebPlayer" ); + return null; +#else + string Data = Export_Google_CreateData(); + + WWWForm form = new WWWForm(); + form.AddField("key", Google_SpreadsheetKey); + form.AddField("action", "SetLanguageSource"); + form.AddField("data", Data); + form.AddField("updateMode", UpdateMode.ToString()); + + #if UNITY_EDITOR + form.AddField("password", Google_Password); +#endif + + + UnityWebRequest www = UnityWebRequest.Post(LocalizationManager.GetWebServiceURL(this), form); + I2Utils.SendWebRequest(www); + return www; + #endif + } + + string Export_Google_CreateData() + { + List Categories = GetCategories(true); + StringBuilder Builder = new StringBuilder(); + + bool bFirst = true; + foreach (string category in Categories) + { + if (bFirst) + bFirst = false; + else + Builder.Append(""); + + #if !UNITY_EDITOR + bool Spreadsheet_SpecializationAsRows = true; + bool Spreadsheet_SortRows = true; + #endif + + string CSV = Export_I2CSV(category, specializationsAsRows:Spreadsheet_SpecializationAsRows, sortRows:Spreadsheet_SortRows); + Builder.Append(category); + Builder.Append(""); + Builder.Append(CSV); + } + return Builder.ToString(); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Export_Google.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Export_Google.cs.meta new file mode 100644 index 00000000..65aa2700 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Export_Google.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 86f9d1c7a0c3815419fc67a9d657ce40 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Import_CSV.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Import_CSV.cs new file mode 100644 index 00000000..982d2b6a --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Import_CSV.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace TEngine.Localization +{ + public partial class LanguageSourceData + { + public string Import_CSV( string Category, string CSVstring, eSpreadsheetUpdateMode UpdateMode = eSpreadsheetUpdateMode.Replace, char Separator = ',' ) + { + List CSV = LocalizationReader.ReadCSV (CSVstring, Separator); + return Import_CSV( Category, CSV, UpdateMode ); + } + + public string Import_I2CSV( string Category, string I2CSVstring, eSpreadsheetUpdateMode UpdateMode = eSpreadsheetUpdateMode.Replace ) + { + List CSV = LocalizationReader.ReadI2CSV (I2CSVstring); + return Import_CSV( Category, CSV, UpdateMode ); + } + + public string Import_CSV( string Category, List CSV, eSpreadsheetUpdateMode UpdateMode = eSpreadsheetUpdateMode.Replace ) + { + string[] Tokens = CSV[0]; + + int LanguagesStartIdx = 1; + int TypeColumnIdx = -1; + int DescColumnIdx = -1; + + var ValidColumnName_Key = new[]{ "Key" }; + var ValidColumnName_Type = new[]{ "Type" }; + var ValidColumnName_Desc = new[]{ "Desc", "Description" }; + + if (Tokens.Length>1 && ArrayContains(Tokens[0], ValidColumnName_Key)) + { + if (UpdateMode == eSpreadsheetUpdateMode.Replace) + ClearAllData(); + + if (Tokens.Length>2) + { + if (ArrayContains(Tokens[1], ValidColumnName_Type)) + { + TypeColumnIdx = 1; + LanguagesStartIdx = 2; + } + if (ArrayContains(Tokens[1], ValidColumnName_Desc)) + { + DescColumnIdx = 1; + LanguagesStartIdx = 2; + } + + } + if (Tokens.Length>3) + { + if (ArrayContains(Tokens[2], ValidColumnName_Type)) + { + TypeColumnIdx = 2; + LanguagesStartIdx = 3; + } + if (ArrayContains(Tokens[2], ValidColumnName_Desc)) + { + DescColumnIdx = 2; + LanguagesStartIdx = 3; + } + } + } + else + return "Bad Spreadsheet Format.\nFirst columns should be 'Key', 'Type' and 'Desc'"; + + int nLanguages = Mathf.Max (Tokens.Length-LanguagesStartIdx, 0); + int[] LanIndices = new int[nLanguages]; + for (int i=0; i0) + { + specialization = sKey.Substring(idx + 1, sKey.Length - idx - 2); + if (specialization == "touch") specialization = "Touch"; + sKey = sKey.Remove(idx); + } + } + ValidateFullTerm(ref sKey); + if (string.IsNullOrEmpty(sKey)) + continue; + + TermData termData = GetTermData(sKey); + + // Check to see if its a new term + if (termData==null) + { + termData = new TermData(); + termData.Term = sKey; + + termData.Languages = new string[ mLanguages.Count ]; + termData.Flags = new byte[ mLanguages.Count ]; + for (int j=0; j0) + termData.TermType = GetTermType(Tokens[TypeColumnIdx]); + + if (DescColumnIdx>0) + termData.Description = Tokens[DescColumnIdx]; + + for (int j = 0; j < LanIndices.Length && j < Tokens.Length - LanguagesStartIdx; ++j) + if (!string.IsNullOrEmpty(Tokens[j + LanguagesStartIdx])) // Only change the translation if there is a new value + { + var lanIdx = LanIndices[j]; + if (lanIdx < 0) + continue; + var value = Tokens[j + LanguagesStartIdx]; + + if (value == "-") + value = string.Empty; + else + if (value == "") + value = null; + + termData.SetTranslation(lanIdx, value, specialization); + } + } + if (Application.isPlaying) + { + SaveLanguages(HasUnloadedLanguages()); + } + return string.Empty; + } + + bool ArrayContains( string MainText, params string[] texts ) + { + for (int i=0, imax=texts.Length; i=0) + return true; + return false; + } + + public static eTermType GetTermType( string type ) + { + for (int i=0, imax=(int)eTermType.Object; i<=imax; ++i) + if (string.Equals( ((eTermType)i).ToString(), type, StringComparison.OrdinalIgnoreCase)) + return (eTermType)i; + + return eTermType.Text; + } + + #region Language Cache format + + void Import_Language_from_Cache(int langIndex, string langData, bool useFallback, bool onlyCurrentSpecialization) + { + int index = 0; + while (index < langData.Length) + { + int nextIndex = langData.IndexOf("[i2t]", index, StringComparison.Ordinal); + if (nextIndex < 0) nextIndex = langData.Length; + + // check for errors + int termNameEnd = langData.IndexOf("=", index, StringComparison.Ordinal); + if (termNameEnd >= nextIndex) + return; + + string termName = langData.Substring(index, termNameEnd - index); + index = termNameEnd+1; + + var termData = GetTermData(termName); + if (termData != null) + { + string translation = null; + + if (index != nextIndex) + { + translation = langData.Substring(index, nextIndex - index); + if (translation.StartsWith("[i2fb]", StringComparison.Ordinal)) + { + translation = useFallback ? translation.Substring(6) : null; + } + if (onlyCurrentSpecialization && translation != null) + { + translation = SpecializationManager.GetSpecializedText(translation); + } + } + termData.Languages[langIndex] = translation; + } + index = nextIndex + 5; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Import_CSV.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Import_CSV.cs.meta new file mode 100644 index 00000000..7c86dd83 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Import_CSV.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 6dfd98b5473aa1a49b82726182b50d08 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Import_Google.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Import_Google.cs new file mode 100644 index 00000000..e5d6cd8c --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Import_Google.cs @@ -0,0 +1,385 @@ +using System; +using System.Collections; +using System.Text; +using UnityEditor; +using UnityEngine; +using UnityEngine.Networking; +using UnityEngine.SceneManagement; +using Object = UnityEngine.Object; + +namespace TEngine.Localization +{ + public partial class LanguageSourceData + { + private string mDelayedGoogleData; // Data that was downloaded and is waiting for a levelLoaded event to apply the localization without a lag in performance + #region Connection to Web Service + + public static void FreeUnusedLanguages() + { + var source = LocalizationManager.Sources[0]; + int langIndex = source.GetLanguageIndex(LocalizationManager.CurrentLanguage); + + for (int i=0; i 19) // Check for corruption from previous versions + savedSpreadsheetVersion = string.Empty; + Google_LastUpdatedVersion = savedSpreadsheetVersion; + + //Debug.Log ("[I2Loc] Using Saved (PlayerPref) data in 'I2Source_"+PlayerPrefName+"'" ); + Import_Google_Result(I2SavedData, eSpreadsheetUpdateMode.Replace); + } + + bool IsNewerVersion( string currentVersion, string newVersion ) + { + if (string.IsNullOrEmpty (newVersion)) // if no new version + return false; + if (string.IsNullOrEmpty (currentVersion)) // there is a new version, but not a current one + return true; + + long currentV, newV; + if (!long.TryParse (newVersion, out newV) || !long.TryParse (currentVersion, out currentV)) // if can't parse either, then force get the new one + return true; + + return newV > currentV; + } + + // When JustCheck is true, importing from google will not download any data, just detect if the Spreadsheet is up-to-date + public void Import_Google( bool ForceUpdate, bool justCheck) + { + if (!ForceUpdate && GoogleUpdateFrequency==eGoogleUpdateFrequency.Never) + return; + + if (!I2Utils.IsPlaying()) + return; + + #if UNITY_EDITOR + if (justCheck && GoogleInEditorCheckFrequency==eGoogleUpdateFrequency.Never) + return; + #endif + + #if UNITY_EDITOR + var updateFrequency = GoogleInEditorCheckFrequency; + #else + var updateFrequency = GoogleUpdateFrequency; + #endif + + string PlayerPrefName = GetSourcePlayerPrefName(); + + if (!ForceUpdate && updateFrequency != eGoogleUpdateFrequency.Always) + { + #if UNITY_EDITOR + string sTimeOfLastUpdate = EditorPrefs.GetString("LastGoogleUpdate_"+PlayerPrefName, ""); + #else + string sTimeOfLastUpdate = PersistentStorage.GetSetting_String("LastGoogleUpdate_"+PlayerPrefName, ""); + #endif + DateTime TimeOfLastUpdate; + try + { + if (DateTime.TryParse( sTimeOfLastUpdate, out TimeOfLastUpdate )) + { + double TimeDifference = (DateTime.Now-TimeOfLastUpdate).TotalDays; + switch (updateFrequency) + { + case eGoogleUpdateFrequency.Daily: if (TimeDifference<1) return; + break; + case eGoogleUpdateFrequency.Weekly: if (TimeDifference<8) return; + break; + case eGoogleUpdateFrequency.Monthly: if (TimeDifference<31) return; + break; + case eGoogleUpdateFrequency.OnlyOnce: return; + case eGoogleUpdateFrequency.EveryOtherDay : if (TimeDifference < 2) return; + break; + } + } + } + catch(Exception) + { } + } + #if UNITY_EDITOR + EditorPrefs.SetString("LastGoogleUpdate_" + PlayerPrefName, DateTime.Now.ToString()); + #else + PersistentStorage.SetSetting_String("LastGoogleUpdate_"+PlayerPrefName, DateTime.Now.ToString()); + #endif + + //--[ Checking google for updated data ]----------------- + CoroutineManager.Start(Import_Google_Coroutine(ForceUpdate, justCheck)); + } + + string GetSourcePlayerPrefName() + { + if (owner == null) + return null; + string sourceName = (owner as Object).name; + if (!string.IsNullOrEmpty(Google_SpreadsheetKey)) + { + sourceName += Google_SpreadsheetKey; + } + // If its a global source, use its name, otherwise, use the name and the level it is in + if (Array.IndexOf(LocalizationManager.GlobalSources, (owner as Object).name)>=0) + return sourceName; +#if UNITY_4_6 || UNITY_4_7 || UNITY_4_8 || UNITY_4_9 || UNITY_5_0 || UNITY_5_1 || UNITY_5_2 + return Application.loadedLevelName + "_" + sourceName; +#else + return SceneManager.GetActiveScene().name+"_"+ sourceName; +#endif + } + + IEnumerator Import_Google_Coroutine(bool forceUpdate, bool JustCheck) + { + UnityWebRequest www = Import_Google_CreateWWWcall(forceUpdate, JustCheck); + if (www==null) + yield break; + + while (!www.isDone) + yield return null; + + //Debug.Log ("Google Result: " + www.text); + byte[] bytes = www.downloadHandler.data; + bool notError = string.IsNullOrEmpty(www.error) && bytes!=null; + + if (notError) + { + string wwwText = Encoding.UTF8.GetString(bytes, 0, bytes.Length); + + bool isEmpty = string.IsNullOrEmpty(wwwText) || wwwText == "\"\""; + + if (JustCheck) + { + if (!isEmpty) + { + Debug.LogWarning("Spreadsheet is not up-to-date and Google Live Synchronization is enabled\nWhen playing in the device the Spreadsheet will be downloaded and translations may not behave as what you see in the editor.\nTo fix this, Import or Export replace to Google"); + GoogleLiveSyncIsUptoDate = false; + } + + yield break; + } + + if (!isEmpty) + { + mDelayedGoogleData = wwwText; + + switch (GoogleUpdateSynchronization) + { + case eGoogleUpdateSynchronization.AsSoonAsDownloaded: + { + ApplyDownloadedDataFromGoogle(); + break; + } + case eGoogleUpdateSynchronization.Manual: + break; + case eGoogleUpdateSynchronization.OnSceneLoaded: + { + SceneManager.sceneLoaded += ApplyDownloadedDataOnSceneLoaded; + break; + } + } + + yield break; + } + } + + if (Event_OnSourceUpdateFromGoogle != null) + Event_OnSourceUpdateFromGoogle(this, false, www.error); + + Debug.Log("Language Source was up-to-date with Google Spreadsheet"); + } + + void ApplyDownloadedDataOnSceneLoaded(UnityEngine.SceneManagement.Scene scene, LoadSceneMode mode) + { + SceneManager.sceneLoaded -= ApplyDownloadedDataOnSceneLoaded; + ApplyDownloadedDataFromGoogle(); + } + + public void ApplyDownloadedDataFromGoogle() + { + if (string.IsNullOrEmpty(mDelayedGoogleData)) + return; + + var errorMsg = Import_Google_Result(mDelayedGoogleData, eSpreadsheetUpdateMode.Replace, true); + if (string.IsNullOrEmpty(errorMsg)) + { + if (Event_OnSourceUpdateFromGoogle != null) + Event_OnSourceUpdateFromGoogle(this, true, ""); + + LocalizationManager.LocalizeAll(true); + Debug.Log("Done Google Sync"); + } + else + { + if (Event_OnSourceUpdateFromGoogle != null) + Event_OnSourceUpdateFromGoogle(this, false, ""); + + Debug.Log("Done Google Sync: source was up-to-date"); + } + } + + public UnityWebRequest Import_Google_CreateWWWcall( bool ForceUpdate, bool justCheck ) + { + if (!HasGoogleSpreadsheet()) + return null; + + string savedVersion = PersistentStorage.GetSetting_String("I2SourceVersion_"+GetSourcePlayerPrefName(), Google_LastUpdatedVersion); + if (savedVersion.Length > 19) // Check for corruption + savedVersion= string.Empty; + +#if !UNITY_EDITOR + if (IsNewerVersion(savedVersion, Google_LastUpdatedVersion)) + Google_LastUpdatedVersion = savedVersion; +#endif + + string query = string.Format("{0}?key={1}&action=GetLanguageSource&version={2}", + LocalizationManager.GetWebServiceURL(this), + Google_SpreadsheetKey, + ForceUpdate ? "0" : Google_LastUpdatedVersion); +#if UNITY_EDITOR + if (justCheck) + { + query += "&justcheck=true"; + } +#endif + UnityWebRequest www = UnityWebRequest.Get(query); + I2Utils.SendWebRequest(www); + return www; + } + + public bool HasGoogleSpreadsheet() + { + return !string.IsNullOrEmpty(Google_WebServiceURL) && !string.IsNullOrEmpty(Google_SpreadsheetKey) && + !string.IsNullOrEmpty(LocalizationManager.GetWebServiceURL(this)); + } + + public string Import_Google_Result( string JsonString, eSpreadsheetUpdateMode UpdateMode, bool saveInPlayerPrefs = false ) + { + try + { + string ErrorMsg = string.Empty; + if (string.IsNullOrEmpty(JsonString) || JsonString == "\"\"") + { + return ErrorMsg; + } + + int idxV = JsonString.IndexOf("version=", StringComparison.Ordinal); + int idxSV = JsonString.IndexOf("script_version=", StringComparison.Ordinal); + if (idxV < 0 || idxSV < 0) + { + return "Invalid Response from Google, Most likely the WebService needs to be updated"; + } + + idxV += "version=".Length; + idxSV += "script_version=".Length; + + string newSpreadsheetVersion = JsonString.Substring(idxV, JsonString.IndexOf(",", idxV, StringComparison.Ordinal) - idxV); + var scriptVersion = int.Parse(JsonString.Substring(idxSV, JsonString.IndexOf(",", idxSV, StringComparison.Ordinal) - idxSV)); + + if (newSpreadsheetVersion.Length > 19) // Check for corruption + newSpreadsheetVersion = string.Empty; + + if (scriptVersion != LocalizationManager.GetRequiredWebServiceVersion()) + { + return "The current Google WebService is not supported.\nPlease, delete the WebService from the Google Drive and Install the latest version."; + } + + //Debug.Log (Google_LastUpdatedVersion + " - " + newSpreadsheetVersion); + if (saveInPlayerPrefs && !IsNewerVersion(Google_LastUpdatedVersion, newSpreadsheetVersion)) +#if UNITY_EDITOR + return ""; +#else + return "LanguageSource is up-to-date"; +#endif + + if (saveInPlayerPrefs) + { + string PlayerPrefName = GetSourcePlayerPrefName(); + PersistentStorage.SaveFile(PersistentStorage.eFileType.Persistent, "I2Source_" + PlayerPrefName + ".loc", "[i2e]" + StringObfucator.Encode(JsonString)); + PersistentStorage.SetSetting_String("I2SourceVersion_" + PlayerPrefName, newSpreadsheetVersion); + PersistentStorage.ForceSaveSettings(); + } + Google_LastUpdatedVersion = newSpreadsheetVersion; + + if (UpdateMode == eSpreadsheetUpdateMode.Replace) + ClearAllData(); + + int CSVstartIdx = JsonString.IndexOf("[i2category]", StringComparison.Ordinal); + while (CSVstartIdx > 0) + { + CSVstartIdx += "[i2category]".Length; + int endCat = JsonString.IndexOf("[/i2category]", CSVstartIdx, StringComparison.Ordinal); + string category = JsonString.Substring(CSVstartIdx, endCat - CSVstartIdx); + endCat += "[/i2category]".Length; + + int endCSV = JsonString.IndexOf("[/i2csv]", endCat, StringComparison.Ordinal); + string csv = JsonString.Substring(endCat, endCSV - endCat); + + CSVstartIdx = JsonString.IndexOf("[i2category]", endCSV, StringComparison.Ordinal); + + Import_I2CSV(category, csv, UpdateMode); + + // Only the first CSV should clear the Data + if (UpdateMode == eSpreadsheetUpdateMode.Replace) + UpdateMode = eSpreadsheetUpdateMode.Merge; + } + + GoogleLiveSyncIsUptoDate = true; + if (I2Utils.IsPlaying()) + { + SaveLanguages(true); + } + + if (!string.IsNullOrEmpty(ErrorMsg)) + Editor_SetDirty(); + return ErrorMsg; + } + catch (Exception e) + { + Debug.LogWarning(e); + return e.ToString(); + } + } + +#endregion + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Import_Google.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Import_Google.cs.meta new file mode 100644 index 00000000..336c8739 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Import_Google.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 637497c3e1b8b944e9a645fe50e27401 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Languages.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Languages.cs new file mode 100644 index 00000000..85fe3df2 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Languages.cs @@ -0,0 +1,335 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +namespace TEngine.Localization +{ + public partial class LanguageSourceData + { + #region Language + + public int GetLanguageIndex( string language, bool AllowDiscartingRegion = true, bool SkipDisabled = true) + { + // First look for an exact match + for (int i=0, imax=mLanguages.Count; iBestSimilitud) + { + BestSimilitud = commonWords; + MostSimilar = i; + } + //if (AreTheSameLanguage(mLanguages[i].Name, language)) + // return i; + } + if (MostSimilar>=0) + return MostSimilar; + } + return -1; + } + + public LanguageData GetLanguageData(string language, bool AllowDiscartingRegion = true) + { + int idx = GetLanguageIndex(language, AllowDiscartingRegion, false); + return idx < 0 ? null : mLanguages[idx]; + } + + // TODO: Fix IsCurrentLanguage when current=English and there are two variants in the source (English Canada, English US) + public bool IsCurrentLanguage( int languageIndex ) + { + return LocalizationManager.CurrentLanguage == mLanguages[languageIndex].Name; + } + + public int GetLanguageIndexFromCode( string Code, bool exactMatch=true, bool ignoreDisabled = false) + { + for (int i = 0, imax = mLanguages.Count; i < imax; ++i) + { + if (ignoreDisabled && !mLanguages[i].IsEnabled()) + continue; + + if (string.Compare(mLanguages[i].Code, Code, StringComparison.OrdinalIgnoreCase) == 0) + return i; + } + + if (!exactMatch) + { + // Find any match without using the Regions + for (int i = 0, imax = mLanguages.Count; i < imax; ++i) + { + if (ignoreDisabled && !mLanguages[i].IsEnabled()) + continue; + + if (string.Compare(mLanguages[i].Code, 0, Code, 0, 2, StringComparison.OrdinalIgnoreCase) == 0) + return i; + } + } + + return -1; + } + + public static int GetCommonWordInLanguageNames(string Language1, string Language2) + { + if (string.IsNullOrEmpty (Language1) || string.IsNullOrEmpty (Language2)) + return 0; + var separators = "( )-/\\".ToCharArray(); + string[] Words1 = Language1.ToLower().Split(separators); + string[] Words2 = Language2.ToLower().Split(separators); + + int similitud = 0; + foreach (var word in Words1) + if (!string.IsNullOrEmpty(word) && Words2.Contains(word)) + similitud++; + + foreach (var word in Words2) + if (!string.IsNullOrEmpty(word) && Words1.Contains(word)) + similitud++; + + return similitud; + } + + public static bool AreTheSameLanguage(string Language1, string Language2) + { + Language1 = GetLanguageWithoutRegion(Language1); + Language2 = GetLanguageWithoutRegion(Language2); + return string.Compare(Language1, Language2, StringComparison.OrdinalIgnoreCase)==0; + } + + public static string GetLanguageWithoutRegion(string Language) + { + int Index = Language.IndexOfAny("(/\\[,{".ToCharArray()); + if (Index<0) + return Language; + return Language.Substring(0, Index).Trim(); + } + + public void AddLanguage(string LanguageName) + { + AddLanguage(LanguageName, GoogleLanguages.GetLanguageCode(LanguageName)); + } + + public void AddLanguage( string LanguageName, string LanguageCode ) + { + if (GetLanguageIndex(LanguageName, false)>=0) + return; + + LanguageData Lang = new LanguageData(); + Lang.Name = LanguageName; + Lang.Code = LanguageCode; + mLanguages.Add (Lang); + + int NewSize = mLanguages.Count; + for (int i=0, imax=mTerms.Count; i GetLanguages( bool skipDisabled = true) + { + List Languages = new List(); + for (int j = 0, jmax = mLanguages.Count; j < jmax; ++j) + { + if (!skipDisabled || mLanguages[j].IsEnabled()) + Languages.Add(mLanguages[j].Name); + } + return Languages; + } + + public List GetLanguagesCode(bool allowRegions = true, bool skipDisabled = true) + { + List Languages = new List(); + for (int j = 0, jmax = mLanguages.Count; j < jmax; ++j) + { + if (skipDisabled && !mLanguages[j].IsEnabled()) + continue; + + var code = mLanguages[j].Code; + + if (!allowRegions && code != null && code.Length > 2) + code = code.Substring(0, 2); + + if (!string.IsNullOrEmpty(code) && !Languages.Contains(code)) + Languages.Add(code); + } + return Languages; + } + + public bool IsLanguageEnabled(string Language) + { + int idx = GetLanguageIndex(Language, false); + return idx >= 0 && mLanguages[idx].IsEnabled(); + } + + public void EnableLanguage(string Language, bool bEnabled) + { + int idx = GetLanguageIndex(Language, false, false); + if (idx >= 0) + mLanguages[idx].SetEnabled(bEnabled); + } + + #endregion + + #region Save/Load Language + + public bool AllowUnloadingLanguages() + { + #if UNITY_EDITOR + return _AllowUnloadingLanguages==eAllowUnloadLanguages.EditorAndDevice; + #else + return _AllowUnloadingLanguages!=eAllowUnloadLanguages.Never; + #endif + } + + string GetSavedLanguageFileName(int languageIndex) + { + if (languageIndex < 0) + return null; + + return "LangSource_" + GetSourcePlayerPrefName() + "_" + mLanguages[languageIndex].Name + ".loc"; + } + public void LoadLanguage( int languageIndex, bool UnloadOtherLanguages, bool useFallback, bool onlyCurrentSpecialization, bool forceLoad ) + { + if (!AllowUnloadingLanguages()) + return; + + // Some consoles don't allow IO access + if (!PersistentStorage.CanAccessFiles()) + return; + + if (languageIndex >= 0 && (forceLoad || !mLanguages[languageIndex].IsLoaded())) + { + var tempPath = GetSavedLanguageFileName(languageIndex); + var langData = PersistentStorage.LoadFile(PersistentStorage.eFileType.Temporal, tempPath, false); + + if (!string.IsNullOrEmpty(langData)) + { + Import_Language_from_Cache(languageIndex, langData, useFallback, onlyCurrentSpecialization); + mLanguages[languageIndex].SetLoaded(true); + } + } + if (UnloadOtherLanguages && I2Utils.IsPlaying()) + { + for (int lan = 0; lan < mLanguages.Count; ++lan) + { + if (lan != languageIndex) + UnloadLanguage(lan); + } + } + } + + // if forceLoad, then the language is loaded from the cache even if its already loaded + // this is needed to cleanup fallbacks + public void LoadAllLanguages(bool forceLoad=false) + { + for (int i = 0; i < mLanguages.Count; ++i) + { + LoadLanguage(i, false, false, false, forceLoad); + } + } + + public void UnloadLanguage(int languageIndex) + { + if (!AllowUnloadingLanguages()) + return; + + // Some consoles don't allow IO access + if (!PersistentStorage.CanAccessFiles()) + return; + + if (!I2Utils.IsPlaying() || + !mLanguages[languageIndex].IsLoaded() || + !mLanguages[languageIndex].CanBeUnloaded() || + IsCurrentLanguage(languageIndex) || + !PersistentStorage.HasFile(PersistentStorage.eFileType.Temporal, GetSavedLanguageFileName(languageIndex))) + { + return; + } + + foreach (var termData in mTerms) + { + termData.Languages[languageIndex] = null; + } + mLanguages[languageIndex].SetLoaded(false); + } + + public void SaveLanguages( bool unloadAll, PersistentStorage.eFileType fileLocation = PersistentStorage.eFileType.Temporal) + { + if (!AllowUnloadingLanguages()) + return; + + // Some consoles don't allow IO access + if (!PersistentStorage.CanAccessFiles()) + return; + + for (int i = 0; i < mLanguages.Count; ++i) + { + if (string.IsNullOrEmpty(mLanguages[i].Name)) + { + Debug.LogError($"Language {i} has no name, please assign a name to the language or it may not show on a build"); + continue; + } + var data = Export_Language_to_Cache(i, IsCurrentLanguage(i)); + if (string.IsNullOrEmpty(data)) + continue; + + PersistentStorage.SaveFile(PersistentStorage.eFileType.Temporal, GetSavedLanguageFileName(i), data); + } + + if (unloadAll) + { + for (int i = 0; i < mLanguages.Count; ++i) + { + if (unloadAll && !IsCurrentLanguage(i)) + UnloadLanguage(i); + } + } + } + + public bool HasUnloadedLanguages() + { + for (int i = 0; i < mLanguages.Count; ++i) + { + if (!mLanguages[i].IsLoaded()) + return true; + } + return false; + + } +#endregion + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Languages.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Languages.cs.meta new file mode 100644 index 00000000..c8f0b8c8 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Languages.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 3c8d05bf03c50f64eb591ea1499fae82 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Misc.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Misc.cs new file mode 100644 index 00000000..d3ca8843 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Misc.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; + +namespace TEngine.Localization +{ + public partial class LanguageSourceData + { + public static string EmptyCategory = "Default"; + public static char[] CategorySeparators = "/\\".ToCharArray(); + + #region Keys + + public List GetCategories( bool OnlyMainCategory = false, List Categories = null ) + { + if (Categories==null) + Categories = new List(); + + foreach (TermData data in mTerms) + { + string sCategory = GetCategoryFromFullTerm( data.Term, OnlyMainCategory ); + if (!Categories.Contains(sCategory)) + Categories.Add(sCategory); + } + Categories.Sort(); + return Categories; + } + + public static string GetKeyFromFullTerm( string FullTerm, bool OnlyMainCategory = false ) + { + int Index = OnlyMainCategory ? FullTerm.IndexOfAny(CategorySeparators) : + FullTerm.LastIndexOfAny(CategorySeparators); + + return Index<0 ? FullTerm :FullTerm.Substring(Index+1); + } + + public static string GetCategoryFromFullTerm( string FullTerm, bool OnlyMainCategory = false ) + { + int Index = OnlyMainCategory ? FullTerm.IndexOfAny(CategorySeparators) : + FullTerm.LastIndexOfAny(CategorySeparators); + + return Index<0 ? EmptyCategory : FullTerm.Substring(0, Index); + } + + public static void DeserializeFullTerm( string FullTerm, out string Key, out string Category, bool OnlyMainCategory = false ) + { + int Index = OnlyMainCategory ? FullTerm.IndexOfAny(CategorySeparators) : + FullTerm.LastIndexOfAny(CategorySeparators); + + if (Index<0) + { + Category = EmptyCategory; + Key = FullTerm; + } + else + { + Category = FullTerm.Substring(0, Index); + Key = FullTerm.Substring(Index+1); + } + } + + #endregion + + #region Misc + + #endregion + + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Misc.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Misc.cs.meta new file mode 100644 index 00000000..47ca03b3 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Misc.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: eaa919dd06da3464082b8f16759a5b16 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Terms.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Terms.cs new file mode 100644 index 00000000..61e3a612 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Terms.cs @@ -0,0 +1,255 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + public partial class LanguageSourceData + { + #region Language + + public void UpdateDictionary(bool force = false) + { + if (!force && mDictionary != null && mDictionary.Count == mTerms.Count) + return; + + StringComparer comparer = CaseInsensitiveTerms ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal; + if (mDictionary.Comparer != comparer) + mDictionary = new Dictionary(comparer); + else + mDictionary.Clear(); + + for (int i = 0, imax = mTerms.Count; i < imax; ++i) + { + var termData = mTerms[i]; + ValidateFullTerm(ref termData.Term); + + mDictionary[termData.Term]= mTerms[i]; + mTerms[i].Validate(); + } + + if (I2Utils.IsPlaying()) + { + SaveLanguages(true); + } + } + + public string GetTranslation (string term, string overrideLanguage = null, string overrideSpecialization = null, bool skipDisabled = false, bool allowCategoryMistmatch = false) + { + TryGetTranslation(term, out string translation, overrideLanguage:overrideLanguage, overrideSpecialization:overrideSpecialization, skipDisabled:skipDisabled, allowCategoryMistmatch:allowCategoryMistmatch); + return translation; + } + + public bool TryGetTranslation (string term, out string Translation, string overrideLanguage=null, string overrideSpecialization=null, bool skipDisabled=false, bool allowCategoryMistmatch=false) + { + int Index = GetLanguageIndex( overrideLanguage==null ? LocalizationManager.CurrentLanguage : overrideLanguage, SkipDisabled: false ); + + if (Index>=0 && (!skipDisabled || mLanguages[Index].IsEnabled())) + { + TermData data = GetTermData(term, allowCategoryMistmatch:allowCategoryMistmatch); + if (data!=null) + { + Translation = data.GetTranslation(Index, overrideSpecialization, editMode:true); + + // "---" is a code to define that the translation is meant to be empty + if (Translation == "---") + { + Translation = string.Empty; + return true; + } + + if (!string.IsNullOrEmpty(Translation)) + { + // has a valid translation + return true; + } + + Translation = null; + } + + if (OnMissingTranslation == MissingTranslationAction.ShowWarning) + { + Translation = $""; + Debug.LogWarning($"Missing Translation for '{term}'", Localize.CurrentLocalizeComponent); + return false; + } + + if (OnMissingTranslation == MissingTranslationAction.Fallback && data!=null) + { + return TryGetFallbackTranslation(data, out Translation, Index, overrideSpecialization, skipDisabled); + } + + if (OnMissingTranslation == MissingTranslationAction.Empty) + { + Translation = string.Empty; + return false; + } + + if (OnMissingTranslation == MissingTranslationAction.ShowTerm) + { + Translation = term; + return false; + } + + } + + Translation = null; + return false; + } + + bool TryGetFallbackTranslation(TermData termData, out string Translation, int langIndex, string overrideSpecialization = null, bool skipDisabled = false) + { + // Find base Language Code + string baseLanguage = mLanguages[langIndex].Code; + if (!string.IsNullOrEmpty(baseLanguage)) + { + if (baseLanguage.Contains("-")) + { + baseLanguage = baseLanguage.Substring(0, baseLanguage.IndexOf('-')); + } + + // Try finding in any of the Region of the base language + for (int i = 0; i < mLanguages.Count; ++i) + { + if (i != langIndex && + mLanguages[i].Code.StartsWith(baseLanguage, StringComparison.Ordinal) && + (!skipDisabled || mLanguages[i].IsEnabled()) ) + { + Translation = termData.GetTranslation(i, overrideSpecialization, editMode: true); + if (!string.IsNullOrEmpty(Translation)) + return true; + } + } + } + + + // Otherwise, Try finding the first active language with a valid translation + for (int i = 0; i < mLanguages.Count; ++i) + { + if (i!=langIndex && + (!skipDisabled || mLanguages[i].IsEnabled()) && + (baseLanguage==null || !mLanguages[i].Code.StartsWith(baseLanguage, StringComparison.Ordinal))) + { + Translation = termData.GetTranslation(i, overrideSpecialization, editMode: true); + if (!string.IsNullOrEmpty(Translation)) + return true; + } + } + Translation = null; + return false; + } + + public TermData AddTerm( string term ) + { + return AddTerm (term, eTermType.Text); + } + + public TermData GetTermData( string term, bool allowCategoryMistmatch = false) + { + if (string.IsNullOrEmpty(term)) + return null; + + if (mDictionary.Count==0)// != mTerms.Count) + UpdateDictionary(); + + TermData data; + if (mDictionary.TryGetValue(term, out data)) + return data; + + TermData d = null; + if (allowCategoryMistmatch) + { + var keyPart = GetKeyFromFullTerm (term); + foreach (var kvp in mDictionary) + if (kvp.Value.IsTerm (keyPart, true)) + { + if (d == null) + d = kvp.Value; + else + return null; + } + } + return d; + } + + public bool ContainsTerm(string term) + { + return GetTermData(term)!=null; + } + + public List GetTermsList ( string Category = null ) + { + if (mDictionary.Count != mTerms.Count) + UpdateDictionary(); + if (string.IsNullOrEmpty( Category )) + return new List( mDictionary.Keys ); + var terms = new List(); + for (int i=0; iEmptyCategory.Length && Term[EmptyCategory.Length]=='/') + Term = Term.Substring(EmptyCategory.Length+1); + } + Term = I2Utils.GetValidTermName(Term, true); + } + + + #endregion + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Terms.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Terms.cs.meta new file mode 100644 index 00000000..640162a8 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LanguageSource/LanguageSourceData_Terms.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: f55c00558b2b4f94086c04e47d98a645 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LocalizationReader.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LocalizationReader.cs new file mode 100644 index 00000000..d0d6ab41 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LocalizationReader.cs @@ -0,0 +1,243 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using UnityEngine; + +namespace TEngine.Localization +{ + public class LocalizationReader + { + #region Dictionary Assets + + public static Dictionary ReadTextAsset( TextAsset asset ) + { + string Text = Encoding.UTF8.GetString (asset.bytes, 0, asset.bytes.Length); + Text = Text.Replace("\r\n", "\n"); + Text = Text.Replace("\r", "\n"); + StringReader reader = new StringReader(Text); + + string s; + Dictionary Dict = new Dictionary(StringComparer.Ordinal); + while ( (s=reader.ReadLine()) != null ) + { + string Key, Value, Category, TermType, Comment; + if (!TextAsset_ReadLine(s, out Key, out Value, out Category, out Comment, out TermType)) + continue; + + if (!string.IsNullOrEmpty(Key) && !string.IsNullOrEmpty(Value)) + Dict[Key]=Value; + } + return Dict; + } + + public static bool TextAsset_ReadLine( string line, out string key, out string value, out string category, out string comment, out string termType ) + { + key = string.Empty; + category= string.Empty; + comment = string.Empty; + termType= string.Empty; + value = string.Empty; + + //--[ Comment ]----------------------- + int iComment = line.LastIndexOf("//", StringComparison.Ordinal); + if (iComment>=0) + { + comment = line.Substring(iComment+2).Trim(); + comment = DecodeString(comment); + line = line.Substring(0, iComment); + } + + //--[ Key ]----------------------------- + int iKeyEnd = line.IndexOf("=", StringComparison.Ordinal); + if (iKeyEnd<0) + { + return false; + } + + key = line.Substring(0, iKeyEnd).Trim(); + value = line.Substring(iKeyEnd+1).Trim(); + value = value.Replace ("\r\n", "\n").Replace ("\n", "\\n"); + value = DecodeString(value); + + //--[ Type ]--------- + if (key.Length>2 && key[0]=='[') + { + int iTypeEnd = key.IndexOf(']'); + if (iTypeEnd>=0) + { + termType = key.Substring(1, iTypeEnd-1); + key = key.Substring(iTypeEnd+1); + } + } + + ValidateFullTerm( ref key ); + + return true; + } + + #endregion + + #region CSV + public static string ReadCSVfile( string Path, Encoding encoding ) + { + string Text = string.Empty; + #if (UNITY_WP8 || UNITY_METRO) && !UNITY_EDITOR + byte[] buffer = UnityEngine.Windows.File.ReadAllBytes (Path); + Text = Encoding.UTF8.GetString(buffer, 0, buffer.Length); + #else + /*using (System.IO.StreamReader reader = System.IO.File.OpenText(Path)) + { + Text = reader.ReadToEnd(); + }*/ + using (var reader = new StreamReader(Path, encoding )) + Text = reader.ReadToEnd(); + #endif + + Text = Text.Replace("\r\n", "\n"); + Text = Text.Replace("\r", "\n"); + + return Text; + } + + public static List ReadCSV( string Text, char Separator=',' ) + { + int iStart = 0; + List CSV = new List(); + + while (iStart < Text.Length) + { + string[] list = ParseCSVline (Text, ref iStart, Separator); + if (list==null) break; + CSV.Add(list); + } + return CSV; + } + + static string[] ParseCSVline( string Line, ref int iStart, char Separator ) + { + List list = new List(); + + //Line = "puig,\"placeres,\"\"cab\nr\nera\"\"algo\"\npuig";//\"Frank\npuig\nplaceres\",aaa,frank\nplaceres"; + + int TextLength = Line.Length; + int iWordStart = iStart; + bool InsideQuote = false; + + while (iStart < TextLength) + { + char c = Line[iStart]; + + if (InsideQuote) + { + if (c=='\"') //--[ Look for Quote End ]------------ + { + if (iStart+1 >= TextLength || Line[iStart+1] != '\"') //-- Single Quote: Quotation Ends + { + InsideQuote = false; + } + else + if (iStart+2 < TextLength && Line[iStart+2]=='\"') //-- Tripple Quotes: Quotation ends + { + InsideQuote = false; + iStart+=2; + } + else + iStart++; // Skip Double Quotes + } + } + + else //-----[ Separators ]---------------------- + + if (c=='\n' || c==Separator) + { + AddCSVtoken(ref list, ref Line, iStart, ref iWordStart); + if (c=='\n') // Stop the row on line breaks + { + iStart++; + break; + } + } + + else //--------[ Start Quote ]-------------------- + + if (c=='\"') + InsideQuote = true; + + iStart++; + } + if (iStart>iWordStart) + AddCSVtoken(ref list, ref Line, iStart, ref iWordStart); + + return list.ToArray(); + } + + static void AddCSVtoken( ref List list, ref string Line, int iEnd, ref int iWordStart) + { + string Text = Line.Substring(iWordStart, iEnd-iWordStart); + iWordStart = iEnd+1; + + Text = Text.Replace("\"\"", "\"" ); + if (Text.Length>1 && Text[0]=='\"' && Text[Text.Length-1]=='\"') + Text = Text.Substring(1, Text.Length-2 ); + + list.Add( Text); + } + + + + #endregion + + #region I2CSV + + public static List ReadI2CSV( string Text ) + { + string[] ColumnSeparator = {"[*]"}; + string[] RowSeparator = {"[ln]"}; + + List CSV = new List(); + foreach (var line in Text.Split (RowSeparator, StringSplitOptions.None)) + CSV.Add (line.Split (ColumnSeparator, StringSplitOptions.None)); + + return CSV; + } + + #endregion + + #region Misc + + public static void ValidateFullTerm( ref string Term ) + { + Term = Term.Replace('\\', '/'); + int First = Term.IndexOf('/'); + if (First<0) + return; + + int second; + while ( (second=Term.LastIndexOf('/')) != First ) + Term = Term.Remove( second,1); + } + + + // this function encodes \r\n and \n into \\n + public static string EncodeString( string str ) + { + if (string.IsNullOrEmpty(str)) + return string.Empty; + + return str.Replace("\r\n", "<\\n>") + .Replace("\r", "<\\n>") + .Replace("\n", "<\\n>"); + } + + public static string DecodeString( string str ) + { + if (string.IsNullOrEmpty(str)) + return string.Empty; + + return str.Replace("<\\n>", "\r\n"); + } + + #endregion + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LocalizationReader.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LocalizationReader.cs.meta new file mode 100644 index 00000000..e6c09794 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LocalizationReader.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 6ec1933af6b4d6947ba69b9bda4260d1 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Localize.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Localize.cs new file mode 100644 index 00000000..af16039c --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Localize.cs @@ -0,0 +1,518 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using UnityEngine.Events; +using Object = UnityEngine.Object; + +#if UNITY_EDITOR +using UnityEditor.Events; +using UnityEditor; +#endif + +namespace TEngine.Localization +{ + [AddComponentMenu("I2/Localization/I2 Localize")] + public class Localize : MonoBehaviour + { + #region Variables: Term + public string Term + { + get { return mTerm; } + set { SetTerm(value); } + } + public string SecondaryTerm + { + get { return mTermSecondary; } + set { SetTerm(null, value); } + } + + public string mTerm = string.Empty, // if Target is a Label, this will be the text, if sprite, this will be the spriteName, etc + mTermSecondary = string.Empty; // if Target is a Label, this will be the font Name, if sprite, this will be the Atlas name, etc + + // This are the terms actually used (will be mTerm/mSecondaryTerm or will get them from the objects if those are missing. e.g. Labels' text and font name) + // This are set when the component starts + [NonSerialized] public string FinalTerm, FinalSecondaryTerm; + + public enum TermModification { DontModify, ToUpper, ToLower, ToUpperFirst, ToTitle/*, CustomRange*/} + public TermModification PrimaryTermModifier = TermModification.DontModify, + SecondaryTermModifier = TermModification.DontModify; + public string TermPrefix, TermSuffix; + + public bool LocalizeOnAwake = true; + + string LastLocalizedLanguage; // Used to avoid Localizing everytime the object is Enabled + +#if UNITY_EDITOR + public ILanguageSource Source; // Source used while in the Editor to preview the Terms (can be of type LanguageSource or LanguageSourceAsset) +#endif + + #endregion + + #region Variables: Target + + public bool IgnoreRTL; // If false, no Right To Left processing will be done + public int MaxCharactersInRTL; // If the language is RTL, the translation will be split in lines not longer than this amount and the RTL fix will be applied per line + public bool IgnoreNumbersInRTL = true; // If the language is RTL, the translation will not convert numbers (will preserve them like: e.g. 123) + + public bool CorrectAlignmentForRTL = true; // If true, when Right To Left language, alignment will be set to Right + + public bool AddSpacesToJoinedLanguages; // Some languages (e.g. Chinese, Japanese and Thai) don't add spaces to their words (all characters are placed toguether), making this variable true, will add spaces to all characters to allow wrapping long texts into multiple lines. + public bool AllowLocalizedParameters=true; + public bool AllowParameters=true; + + #endregion + + #region Variables: References + + public List TranslatedObjects = new List(); // For targets that reference objects (e.g. AudioSource, UITexture,etc) + // this keeps a reference to the possible options. + // If the value is not the name of any of this objects then it will try to load the object from the Resources + + + [NonSerialized] public Dictionary mAssetDictionary = new Dictionary(StringComparer.Ordinal); //This is used to overcome the issue with Unity not serializing Dictionaries + + #endregion + + #region Variable Translation Modifiers + + + public UnityEvent LocalizeEvent = new UnityEvent(); // This allows scripts to modify the translations : e.g. "Player {0} wins" -> "Player Red wins" + + + public static string MainTranslation, SecondaryTranslation; // The callback should use and modify this variables + public static string CallBackTerm, CallBackSecondaryTerm; // during the callback, this will hold the FinalTerm and FinalSecondary to know what terms are originating the translation + public static Localize CurrentLocalizeComponent; // while in the LocalizeCallBack, this points to the Localize calling the callback + + public bool AlwaysForceLocalize; // Force localization when the object gets enabled (useful for callbacks and parameters that change the localization even through the language is the same as in the previous time it was localized) + + [SerializeField] public EventCallback LocalizeCallBack = new EventCallback(); //LocalizeCallBack is deprecated. Please use LocalizeEvent instead. + + #endregion + + #region Variables: Editor Related + public bool mGUI_ShowReferences; + public bool mGUI_ShowTems = true; + public bool mGUI_ShowCallback; + #endregion + + #region Variables: Runtime (LocalizeTarget) + + public ILocalizeTarget mLocalizeTarget; + public string mLocalizeTargetName; // Used to resolve multiple targets in a prefab + + #endregion + + #region Localize + + void Awake() + { + #if UNITY_EDITOR + if (BuildPipeline.isBuildingPlayer) + return; + #endif + + UpdateAssetDictionary(); + FindTarget(); + + if (LocalizeOnAwake) + OnLocalize(); + } + + #if UNITY_EDITOR + void OnValidate() + { + if (LocalizeCallBack.HasCallback()) + { + try + { + var methodInfo = UnityEventBase.GetValidMethodInfo(LocalizeCallBack.Target, LocalizeCallBack.MethodName, Array.Empty()); + + if (methodInfo != null) + { + UnityAction methodDelegate = Delegate.CreateDelegate(typeof(UnityAction), LocalizeCallBack.Target, methodInfo, false) as UnityAction; + if (methodDelegate != null) + UnityEventTools.AddPersistentListener(LocalizeEvent, methodDelegate); + } + } + catch(Exception) + {} + + LocalizeCallBack.Target = null; + LocalizeCallBack.MethodName = null; + } + } + #endif + + void OnEnable() + { + OnLocalize (); + } + + public bool HasCallback() + { + if (LocalizeCallBack.HasCallback()) + return true; + return LocalizeEvent.GetPersistentEventCount() > 0; + } + + public void OnLocalize( bool Force = false ) + { + if (!Force && (!enabled || gameObject==null || !gameObject.activeInHierarchy)) + return; + + if (string.IsNullOrEmpty(LocalizationManager.CurrentLanguage)) + return; + + if (!AlwaysForceLocalize && !Force && !HasCallback() && LastLocalizedLanguage==LocalizationManager.CurrentLanguage) + return; + LastLocalizedLanguage = LocalizationManager.CurrentLanguage; + + // These are the terms actually used (will be mTerm/mSecondaryTerm or will get them from the objects if those are missing. e.g. Labels' text and font name) + if (string.IsNullOrEmpty(FinalTerm) || string.IsNullOrEmpty(FinalSecondaryTerm)) + GetFinalTerms( out FinalTerm, out FinalSecondaryTerm ); + + + bool hasCallback = I2Utils.IsPlaying() && HasCallback(); + + if (!hasCallback && string.IsNullOrEmpty (FinalTerm) && string.IsNullOrEmpty (FinalSecondaryTerm)) + return; + + CurrentLocalizeComponent = this; + CallBackTerm = FinalTerm; + CallBackSecondaryTerm = FinalSecondaryTerm; + MainTranslation = string.IsNullOrEmpty(FinalTerm) || FinalTerm=="-" ? null : LocalizationManager.GetTranslation (FinalTerm, false); + SecondaryTranslation = string.IsNullOrEmpty(FinalSecondaryTerm) || FinalSecondaryTerm == "-" ? null : LocalizationManager.GetTranslation (FinalSecondaryTerm, false); + + if (!hasCallback && /*string.IsNullOrEmpty (MainTranslation)*/ string.IsNullOrEmpty(FinalTerm) && string.IsNullOrEmpty (SecondaryTranslation)) + return; + + { + LocalizeCallBack.Execute (this); // This allows scripts to modify the translations : e.g. "Player {0} wins" -> "Player Red wins" + LocalizeEvent.Invoke(); + if (AllowParameters) + LocalizationManager.ApplyLocalizationParams (ref MainTranslation, gameObject, AllowLocalizedParameters); + } + + if (!FindTarget()) + return; + bool applyRTL = LocalizationManager.IsRight2Left && !IgnoreRTL; + + if (MainTranslation != null) + { + switch (PrimaryTermModifier) + { + case TermModification.ToUpper: MainTranslation = MainTranslation.ToUpper(); break; + case TermModification.ToLower: MainTranslation = MainTranslation.ToLower(); break; + case TermModification.ToUpperFirst: MainTranslation = GoogleTranslation.UppercaseFirst(MainTranslation); break; + case TermModification.ToTitle: MainTranslation = GoogleTranslation.TitleCase(MainTranslation); break; + } + if (!string.IsNullOrEmpty(TermPrefix)) + MainTranslation = applyRTL ? MainTranslation + TermPrefix : TermPrefix + MainTranslation; + if (!string.IsNullOrEmpty(TermSuffix)) + MainTranslation = applyRTL ? TermSuffix + MainTranslation : MainTranslation + TermSuffix; + + if (AddSpacesToJoinedLanguages && LocalizationManager.HasJoinedWords && !string.IsNullOrEmpty(MainTranslation)) + { + var sb = new StringBuilder(); + sb.Append(MainTranslation[0]); + for (int i = 1, imax = MainTranslation.Length; i < imax; ++i) + { + sb.Append(' '); + sb.Append(MainTranslation[i]); + } + + MainTranslation = sb.ToString(); + } + if (applyRTL && mLocalizeTarget.AllowMainTermToBeRTL() && !string.IsNullOrEmpty(MainTranslation)) + MainTranslation = LocalizationManager.ApplyRTLfix(MainTranslation, MaxCharactersInRTL, IgnoreNumbersInRTL); + + } + + if (SecondaryTranslation != null) + { + switch (SecondaryTermModifier) + { + case TermModification.ToUpper: SecondaryTranslation = SecondaryTranslation.ToUpper(); break; + case TermModification.ToLower: SecondaryTranslation = SecondaryTranslation.ToLower(); break; + case TermModification.ToUpperFirst: SecondaryTranslation = GoogleTranslation.UppercaseFirst(SecondaryTranslation); break; + case TermModification.ToTitle: SecondaryTranslation = GoogleTranslation.TitleCase(SecondaryTranslation); break; + } + if (applyRTL && mLocalizeTarget.AllowSecondTermToBeRTL() && !string.IsNullOrEmpty(SecondaryTranslation)) + SecondaryTranslation = LocalizationManager.ApplyRTLfix(SecondaryTranslation); + } + + if (LocalizationManager.HighlightLocalizedTargets) + { + MainTranslation = "LOC:" + FinalTerm; + } + + mLocalizeTarget.DoLocalize( this, MainTranslation, SecondaryTranslation ); + + CurrentLocalizeComponent = null; + } + + #endregion + + #region Finding Target + + public bool FindTarget() + { + if (mLocalizeTarget != null && mLocalizeTarget.IsValid(this)) + return true; + + if (mLocalizeTarget!=null) + { + DestroyImmediate(mLocalizeTarget); + mLocalizeTarget = null; + mLocalizeTargetName = null; + } + + if (!string.IsNullOrEmpty(mLocalizeTargetName)) + { + foreach (var desc in LocalizationManager.mLocalizeTargets) + { + if (mLocalizeTargetName == desc.GetTargetType().ToString()) + { + if (desc.CanLocalize(this)) + mLocalizeTarget = desc.CreateTarget(this); + if (mLocalizeTarget!=null) + return true; + } + } + } + + foreach (var desc in LocalizationManager.mLocalizeTargets) + { + if (!desc.CanLocalize(this)) + continue; + mLocalizeTarget = desc.CreateTarget(this); + mLocalizeTargetName = desc.GetTargetType().ToString(); + if (mLocalizeTarget != null) + return true; + } + + return false; + } + + #endregion + + #region Finding Term + + // Returns the term that will actually be translated + // its either the Term value in this class or the text of the label if there is no term + public void GetFinalTerms( out string primaryTerm, out string secondaryTerm ) + { + primaryTerm = string.Empty; + secondaryTerm = string.Empty; + + if (!FindTarget()) + return; + + + // if either the primary or secondary term is missing, get them. (e.g. from the label's text and font name) + if (mLocalizeTarget != null) + { + mLocalizeTarget.GetFinalTerms(this, mTerm, mTermSecondary, out primaryTerm, out secondaryTerm); + primaryTerm = I2Utils.GetValidTermName(primaryTerm); + } + + // If there are values already set, go with those + if (!string.IsNullOrEmpty(mTerm)) + primaryTerm = mTerm; + + if (!string.IsNullOrEmpty(mTermSecondary)) + secondaryTerm = mTermSecondary; + + if (primaryTerm != null) + primaryTerm = primaryTerm.Trim(); + if (secondaryTerm != null) + secondaryTerm = secondaryTerm.Trim(); + } + + public string GetMainTargetsText() + { + string primary = null, secondary = null; + + if (mLocalizeTarget!=null) + mLocalizeTarget.GetFinalTerms( this, null, null, out primary, out secondary ); + + return string.IsNullOrEmpty(primary) ? mTerm : primary; + } + + public void SetFinalTerms( string Main, string Secondary, out string primaryTerm, out string secondaryTerm, bool RemoveNonASCII ) + { + primaryTerm = RemoveNonASCII ? I2Utils.GetValidTermName(Main) : Main; + secondaryTerm = Secondary; + } + + #endregion + + #region Misc + + public void SetTerm (string primary) + { + if (!string.IsNullOrEmpty(primary)) + FinalTerm = mTerm = primary; + + OnLocalize (true); + } + + public void SetTerm(string primary, string secondary ) + { + if (!string.IsNullOrEmpty(primary)) + FinalTerm = mTerm = primary; + FinalSecondaryTerm = mTermSecondary = secondary; + + OnLocalize(true); + } + + internal T GetSecondaryTranslatedObj( ref string mainTranslation, ref string secondaryTranslation ) where T: Object + { + string newMain, newSecond; + + //--[ Allow main translation to override Secondary ]------------------- + DeserializeTranslation(mainTranslation, out newMain, out newSecond); + + T obj = null; + + if (!string.IsNullOrEmpty(newSecond)) + { + obj = GetObject(newSecond); + if (obj != null) + { + mainTranslation = newMain; + secondaryTranslation = newSecond; + } + } + + if (obj == null) + obj = GetObject(secondaryTranslation); + + return obj; + } + + public void UpdateAssetDictionary() + { + TranslatedObjects.RemoveAll(x => x == null); + mAssetDictionary = TranslatedObjects.Distinct() + .GroupBy(o => o.name) + .ToDictionary(g => g.Key, g => g.First()); + } + + internal T GetObject( string Translation) where T: Object + { + if (string.IsNullOrEmpty (Translation)) + return null; + T obj = GetTranslatedObject(Translation); + + //if (obj==null) + //{ + // Remove path and search by name + //int Index = Translation.LastIndexOfAny("/\\".ToCharArray()); + //if (Index>=0) + //{ + // Translation = Translation.Substring(Index+1); + // obj = GetTranslatedObject(Translation); + //} + //} + return obj; + } + + T GetTranslatedObject( string Translation) where T: Object + { + T Obj = FindTranslatedObject(Translation); + /*if (Obj == null) + return null; + + if ((Obj as T) != null) + return Obj as T; + + // If the found Obj is not of type T, then try finding a component inside + if (Obj as Component != null) + return (Obj as Component).GetComponent(typeof(T)) as T; + + if (Obj as GameObject != null) + return (Obj as GameObject).GetComponent(typeof(T)) as T; + */ + return Obj; + } + + + // translation format: "[secondary]value" [secondary] is optional + void DeserializeTranslation( string translation, out string value, out string secondary ) + { + if (!string.IsNullOrEmpty(translation) && translation.Length>1 && translation[0]=='[') + { + int Index = translation.IndexOf(']'); + if (Index>0) + { + secondary = translation.Substring(1, Index-1); + value = translation.Substring(Index+1); + return; + } + } + value = translation; + secondary = string.Empty; + } + + public T FindTranslatedObject( string value) where T : Object + { + if (string.IsNullOrEmpty(value)) + return null; + + if (mAssetDictionary == null || mAssetDictionary.Count != TranslatedObjects.Count) + { + UpdateAssetDictionary(); + } + + foreach (var kvp in mAssetDictionary) + { + if (kvp.Value is T && value.EndsWith(kvp.Key, StringComparison.OrdinalIgnoreCase)) + { + // Check if the value is just the name or has a path + if (string.Compare(value, kvp.Key, StringComparison.OrdinalIgnoreCase)==0) + return (T) kvp.Value; + + // Check if the path matches + //Resources.get TranslatedObjects[i]. + } + } + + T obj = LocalizationManager.FindAsset(value) as T; + if (obj) + return obj; + + obj = ResourceManager.pInstance.GetAsset(value); + return obj; + } + + public bool HasTranslatedObject( Object Obj ) + { + if (TranslatedObjects.Contains(Obj)) + return true; + return ResourceManager.pInstance.HasAsset(Obj); + + } + + public void AddTranslatedObject( Object Obj ) + { + if (TranslatedObjects.Contains(Obj)) + return; + TranslatedObjects.Add(Obj); + UpdateAssetDictionary(); + } + + #endregion + + #region Utilities + // This can be used to set the language when a button is clicked + public void SetGlobalLanguage( string Language ) + { + LocalizationManager.CurrentLanguage = Language; + } + + #endregion + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Localize.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Localize.cs.meta new file mode 100644 index 00000000..af8cbcc7 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Localize.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 344445a89b4f74a0e9a0a766903df87e +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LocalizeDropdown.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LocalizeDropdown.cs new file mode 100644 index 00000000..ba040203 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LocalizeDropdown.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using TMPro; +using UnityEngine; +using UnityEngine.UI; + +namespace TEngine.Localization +{ + #if !UNITY_5_0 && !UNITY_5_1 + [AddComponentMenu("I2/Localization/Localize Dropdown")] + public class LocalizeDropdown : MonoBehaviour + { + public List _Terms = new List(); + + public void Start() + { + LocalizationManager.OnLocalizeEvent += OnLocalize; + OnLocalize(); + } + + public void OnDestroy() + { + LocalizationManager.OnLocalizeEvent -= OnLocalize; + } + + void OnEnable() + { + if (_Terms.Count == 0) + FillValues(); + OnLocalize (); + } + + public void OnLocalize() + { + if (!enabled || gameObject==null || !gameObject.activeInHierarchy) + return; + + if (string.IsNullOrEmpty(LocalizationManager.CurrentLanguage)) + return; + + UpdateLocalization(); + } + + void FillValues() + { + var _Dropdown = GetComponent(); + if (_Dropdown == null && I2Utils.IsPlaying()) + { + #if TextMeshPro + FillValuesTMPro(); + #endif + return; + } + + foreach (var term in _Dropdown.options) + { + _Terms.Add(term.text); + } + } + + public void UpdateLocalization() + { + var _Dropdown = GetComponent(); + if (_Dropdown == null) + { + #if TextMeshPro + UpdateLocalizationTMPro(); + #endif + return; + } + + _Dropdown.options.Clear(); + foreach (var term in _Terms) + { + var translation = LocalizationManager.GetTranslation(term); + _Dropdown.options.Add( new Dropdown.OptionData( translation ) ); + } + _Dropdown.RefreshShownValue(); + } + + #if TextMeshPro + public void UpdateLocalizationTMPro() + { + var _Dropdown = GetComponent(); + if (_Dropdown == null) + return; + + _Dropdown.options.Clear(); + foreach (var term in _Terms) + { + var translation = LocalizationManager.GetTranslation(term); + _Dropdown.options.Add(new TMP_Dropdown.OptionData(translation)); + } + _Dropdown.RefreshShownValue(); + } + + void FillValuesTMPro() + { + var _Dropdown = GetComponent(); + if (_Dropdown == null) + return; + + foreach (var term in _Dropdown.options) + { + _Terms.Add(term.text); + } + } +#endif + + } +#endif +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LocalizeDropdown.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LocalizeDropdown.cs.meta new file mode 100644 index 00000000..f5f29d20 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/LocalizeDropdown.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: a5379d3aeaf18a24fa23c26a749edfe5 +timeCreated: 1466568092 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager.meta new file mode 100644 index 00000000..616377c8 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: ba131beaf0aba9d4084aff4ea6e742c3 +folderAsset: yes +timeCreated: 1516397531 +licenseType: Store +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager.cs new file mode 100644 index 00000000..4e09a8ad --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager.cs @@ -0,0 +1,92 @@ +using System; +using System.IO; +using System.Linq; +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + public static partial class LocalizationManager + { + + #region Variables: Misc + + #endregion + + public static void InitializeIfNeeded() + { + #if UNITY_EDITOR + #if UNITY_2017_2_OR_NEWER + EditorApplication.playModeStateChanged -= OnEditorPlayModeStateChanged; + EditorApplication.playModeStateChanged += OnEditorPlayModeStateChanged; + #else + UnityEditor.EditorApplication.playmodeStateChanged -= OldOnEditorPlayModeStateChanged; + UnityEditor.EditorApplication.playmodeStateChanged += OldOnEditorPlayModeStateChanged; + #endif + #endif + + if (string.IsNullOrEmpty(mCurrentLanguage) || Sources.Count == 0) + { + AutoLoadGlobalParamManagers(); + UpdateSources(); + SelectStartupLanguage(); + } + } + + public static string GetVersion() + { + return "2.8.22 f4"; + } + + public static int GetRequiredWebServiceVersion() + { + return 5; + } + + public static string GetWebServiceURL( LanguageSourceData source = null ) + { + if (source != null && !string.IsNullOrEmpty(source.Google_WebServiceURL)) + return source.Google_WebServiceURL; + + InitializeIfNeeded(); + for (int i = 0; i < Sources.Count; ++i) + if (Sources[i] != null && !string.IsNullOrEmpty(Sources[i].Google_WebServiceURL)) + return Sources[i].Google_WebServiceURL; + return string.Empty; + } + +#if UNITY_EDITOR + #if UNITY_2017_2_OR_NEWER + static void OnEditorPlayModeStateChanged( PlayModeStateChange stateChange ) + { + if (stateChange != PlayModeStateChange.ExitingPlayMode) + return; + #else + static void OldOnEditorPlayModeStateChanged() + { + if (UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode) + return; + #endif + + OnLocalizeEvent = null; + + foreach (var source in Sources) + { + source.LoadAllLanguages(true); + } + try + { + var tempPath = Application.temporaryCachePath; + + foreach (var file in Directory.GetFiles(tempPath).Where(f => f.Contains("LangSource_") && f.EndsWith(".loc", StringComparison.Ordinal))) + { + File.Delete(file); + } + } + catch(Exception) + { + } + } +#endif + } +} diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager.cs.meta new file mode 100644 index 00000000..89de7d0b --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: dd42f89f650a540d9bd641f752368e27 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Language.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Language.cs new file mode 100644 index 00000000..3cdb0ecc --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Language.cs @@ -0,0 +1,348 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; + +namespace TEngine.Localization +{ + public static partial class LocalizationManager + { + #region Variables: CurrentLanguage + + public static string CurrentLanguage + { + get { + InitializeIfNeeded(); + return mCurrentLanguage; + } + set { + InitializeIfNeeded(); + string SupportedLanguage = GetSupportedLanguage(value); + if (!string.IsNullOrEmpty(SupportedLanguage) && mCurrentLanguage != SupportedLanguage) + { + SetLanguageAndCode(SupportedLanguage, GetLanguageCode(SupportedLanguage)); + } + } + } + public static string CurrentLanguageCode + { + get { + InitializeIfNeeded(); + return mLanguageCode; } + set { + InitializeIfNeeded(); + if (mLanguageCode != value) + { + string LanName = GetLanguageFromCode(value); + if (!string.IsNullOrEmpty(LanName)) + SetLanguageAndCode(LanName, value); + } + } + } + + // "English (United States)" (get returns "United States") + // when set "Canada", the new language code will be "English (Canada)" + public static string CurrentRegion + { + get { + var Lan = CurrentLanguage; + int idx = Lan.IndexOfAny("/\\".ToCharArray()); + if (idx > 0) + return Lan.Substring(idx + 1); + + idx = Lan.IndexOfAny("[(".ToCharArray()); + int idx2 = Lan.LastIndexOfAny("])".ToCharArray()); + if (idx > 0 && idx != idx2) + return Lan.Substring(idx + 1, idx2 - idx - 1); + return string.Empty; + } + set { + var Lan = CurrentLanguage; + int idx = Lan.IndexOfAny("/\\".ToCharArray()); + if (idx > 0) + { + CurrentLanguage = Lan.Substring(idx + 1) + value; + return; + } + + idx = Lan.IndexOfAny("[(".ToCharArray()); + int idx2 = Lan.LastIndexOfAny("])".ToCharArray()); + if (idx > 0 && idx != idx2) + Lan = Lan.Substring(idx); + + CurrentLanguage = Lan + "(" + value + ")"; + } + } + + // "en-US" (get returns "US") (when set "CA", the new language code will be "en-CA") + public static string CurrentRegionCode + { + get { + var code = CurrentLanguageCode; + int idx = code.IndexOfAny(" -_/\\".ToCharArray()); + return idx < 0 ? string.Empty : code.Substring(idx + 1); + } + set { + var code = CurrentLanguageCode; + int idx = code.IndexOfAny(" -_/\\".ToCharArray()); + if (idx > 0) + code = code.Substring(0, idx); + + CurrentLanguageCode = code + "-" + value; + } + } + + public static CultureInfo CurrentCulture + { + get + { + return mCurrentCulture; + } + } + + static string mCurrentLanguage; + static string mLanguageCode; + static CultureInfo mCurrentCulture; + static bool mChangeCultureInfo; + + public static bool IsRight2Left; + public static bool HasJoinedWords; // Some languages (e.g. Chinese, Japanese and Thai) don't add spaces to their words (all characters are placed toguether) + + #endregion + + public static void SetLanguageAndCode(string LanguageName, string LanguageCode, bool RememberLanguage = true, bool Force = false) + { + if (mCurrentLanguage != LanguageName || mLanguageCode != LanguageCode || Force) + { + if (RememberLanguage) + PersistentStorage.SetSetting_String("I2 Language", LanguageName); + mCurrentLanguage = LanguageName; + mLanguageCode = LanguageCode; + mCurrentCulture = CreateCultureForCode(LanguageCode); + if (mChangeCultureInfo) + SetCurrentCultureInfo(); + + IsRight2Left = IsRTL(mLanguageCode); + HasJoinedWords = GoogleLanguages.LanguageCode_HasJoinedWord(mLanguageCode); + LocalizeAll(Force); + } + } + + static CultureInfo CreateCultureForCode(string code) + { +#if !NETFX_CORE + try + { + return CultureInfo.CreateSpecificCulture(code); + } + catch (Exception) + { + return CultureInfo.InvariantCulture; + } +#else + return CultureInfo.InvariantCulture; +#endif + } + + public static void EnableChangingCultureInfo(bool bEnable) + { + if (!mChangeCultureInfo && bEnable) + SetCurrentCultureInfo(); + mChangeCultureInfo = bEnable; + } + + static void SetCurrentCultureInfo() + { + #if !NETFX_CORE + Thread.CurrentThread.CurrentCulture = mCurrentCulture; + #endif + } + + + static void SelectStartupLanguage() + { + if (Sources.Count == 0) + return; + + // Use the system language if there is a source with that language, + // or pick any of the languages provided by the sources + + string SavedLanguage = PersistentStorage.GetSetting_String("I2 Language", string.Empty); + string SysLanguage = GetCurrentDeviceLanguage(); + + // Try selecting the System Language + // But fallback to the first language found if the System Language is not available in any source + + if (!string.IsNullOrEmpty(SavedLanguage) && HasLanguage(SavedLanguage, Initialize: false, SkipDisabled:true)) + { + SetLanguageAndCode(SavedLanguage, GetLanguageCode(SavedLanguage)); + return; + } + + if (!Sources [0].IgnoreDeviceLanguage) + { + // Check if the device language is supported. + // Also recognize when not region is set ("English (United State") will be used if sysLanguage is "English") + string ValidLanguage = GetSupportedLanguage (SysLanguage, true); + if (!string.IsNullOrEmpty (ValidLanguage)) { + SetLanguageAndCode (ValidLanguage, GetLanguageCode (ValidLanguage), false); + return; + } + } + + //--[ Use first language that its not disabled ]----------- + for (int i = 0, imax = Sources.Count; i < imax; ++i) + if (Sources[i].mLanguages.Count > 0) + { + for (int j = 0; j < Sources[i].mLanguages.Count; ++j) + if (Sources[i].mLanguages[j].IsEnabled()) + { + SetLanguageAndCode(Sources[i].mLanguages[j].Name, Sources[i].mLanguages[j].Code, false); + return; + } + } + } + + + public static bool HasLanguage( string Language, bool AllowDiscartingRegion = true, bool Initialize=true, bool SkipDisabled=true ) + { + if (Initialize) + InitializeIfNeeded(); + + // First look for an exact match + for (int i=0, imax=Sources.Count; i=0) + return true; + + // Then allow matching "English (Canada)" to "english" + if (AllowDiscartingRegion) + { + for (int i=0, imax=Sources.Count; i=0) + return true; + } + return false; + } + + // Returns the provided language or a similar one without the Region + //(e.g. "English (Canada)" could be mapped to "english" or "English (United States)" if "English (Canada)" is not found + public static string GetSupportedLanguage( string Language, bool ignoreDisabled=false ) + { + // First try finding the language that matches one of the official languages + string code = GoogleLanguages.GetLanguageCode(Language); + if (!string.IsNullOrEmpty(code)) + { + // First try finding if the exact language code is in one source + for (int i = 0, imax = Sources.Count; i < imax; ++i) + { + int Idx = Sources[i].GetLanguageIndexFromCode(code, true, ignoreDisabled); + if (Idx >= 0) + return Sources[i].mLanguages[Idx].Name; + } + + // If not, try checking without the region + for (int i = 0, imax = Sources.Count; i < imax; ++i) + { + int Idx = Sources[i].GetLanguageIndexFromCode(code, false, ignoreDisabled); + if (Idx >= 0) + return Sources[i].mLanguages[Idx].Name; + } + } + + // If not found, then try finding an exact match for the name + for (int i=0, imax=Sources.Count; i=0) + return Sources[i].mLanguages[Idx].Name; + } + + // Then allow matching "English (Canada)" to "english" + for (int i=0, imax=Sources.Count; i=0) + return Sources[i].mLanguages[Idx].Name; + } + + return string.Empty; + } + + public static string GetLanguageCode( string Language ) + { + if (Sources.Count==0) + UpdateSources(); + for (int i=0, imax=Sources.Count; i=0) + return Sources[i].mLanguages[Idx].Code; + } + return string.Empty; + } + + public static string GetLanguageFromCode( string Code, bool exactMatch=true ) + { + if (Sources.Count==0) + UpdateSources(); + for (int i=0, imax=Sources.Count; i=0) + return Sources[i].mLanguages[Idx].Name; + } + return string.Empty; + } + + + public static List GetAllLanguages ( bool SkipDisabled = true ) + { + if (Sources.Count==0) + UpdateSources(); + List Languages = new List (); + for (int i=0, imax=Sources.Count; i!Languages.Contains(x))); + } + return Languages; + } + + public static List GetAllLanguagesCode(bool allowRegions=true, bool SkipDisabled = true) + { + List Languages = new List(); + for (int i = 0, imax = Sources.Count; i < imax; ++i) + { + Languages.AddRange(Sources[i].GetLanguagesCode(allowRegions, SkipDisabled).Where(x => !Languages.Contains(x))); + } + return Languages; + } + + public static bool IsLanguageEnabled(string Language) + { + for (int i = 0, imax = Sources.Count; i < imax; ++i) + if (!Sources[i].IsLanguageEnabled(Language)) + return false; + return true; + } + + static void LoadCurrentLanguage() + { + for (int i = 0; i < Sources.Count; ++i) + { + var iCurrentLang = Sources[i].GetLanguageIndex(mCurrentLanguage, true, false); + Sources[i].LoadLanguage(iCurrentLang, true, true, true, false); + } + } + + + // This function should only be called from within the Localize Inspector to temporaly preview that Language + public static void PreviewLanguage(string NewLanguage) + { + mCurrentLanguage = NewLanguage; + mLanguageCode = GetLanguageCode(mCurrentLanguage); + IsRight2Left = IsRTL(mLanguageCode); + HasJoinedWords = GoogleLanguages.LanguageCode_HasJoinedWord(mLanguageCode); + } + } +} diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Language.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Language.cs.meta new file mode 100644 index 00000000..556a3911 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Language.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 7d629295da7add24e9465e25cd212bf5 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Parameters.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Parameters.cs new file mode 100644 index 00000000..99e5ddc1 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Parameters.cs @@ -0,0 +1,195 @@ +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace TEngine.Localization +{ + public static partial class LocalizationManager + { + #region Variables: Misc + + public static List ParamManagers = new List(); + + + // returns true if this replaces the normal ApplyLocalizationParams + // returns false if after running this function the manager should also run the default ApplyLocalizationParams to replace parameters + public delegate bool FnCustomApplyLocalizationParams(ref string translation, _GetParam getParam, bool allowLocalizedParameters); + public static FnCustomApplyLocalizationParams CustomApplyLocalizationParams; + #endregion + + #region Parameters + + public delegate object _GetParam(string param); + + public static void AutoLoadGlobalParamManagers() + { + foreach (var manager in Object.FindObjectsOfType()) + { + if (manager._IsGlobalManager && !ParamManagers.Contains(manager)) + { + Debug.Log(manager); + ParamManagers.Add(manager); + } + } + } + + public static void ApplyLocalizationParams(ref string translation, bool allowLocalizedParameters = true) + { + ApplyLocalizationParams(ref translation, p => GetLocalizationParam(p, null), allowLocalizedParameters); + } + + + public static void ApplyLocalizationParams(ref string translation, GameObject root, bool allowLocalizedParameters = true) + { + ApplyLocalizationParams(ref translation, p => GetLocalizationParam(p, root), allowLocalizedParameters); + } + + public static void ApplyLocalizationParams(ref string translation, Dictionary parameters, bool allowLocalizedParameters = true) + { + ApplyLocalizationParams(ref translation, p => { + object o = null; + if (parameters.TryGetValue(p, out o)) + return o; + return null; + }, allowLocalizedParameters); + } + + + public static void ApplyLocalizationParams(ref string translation, _GetParam getParam, bool allowLocalizedParameters=true) + { + if (translation == null) + return; + + bool skip_processing = CustomApplyLocalizationParams!=null && CustomApplyLocalizationParams.Invoke(ref translation, getParam, allowLocalizedParameters); + if (skip_processing) return; + + string pluralType=null; + int idx0 = 0; + int idx1 = translation.Length; + + int index = 0; + while (index>=0 && index0 && isubParam= 0) + { + result = termData.GetTranslation(idx); + } + } + } + + var paramTag = translation.Substring(iParamStart, iParamEnd - iParamStart + 2); + translation = translation.Replace(paramTag, result); + + int amount = 0; + if (int.TryParse(result, out amount)) + { + pluralType = GoogleLanguages.GetPluralType(CurrentLanguageCode, amount).ToString(); + } + + index = iParamStart + result.Length; + } + else + { + index = iParamEnd + 2; + } + } + + if (pluralType != null) + { + var tag = "[i2p_" + pluralType + "]"; + idx0 = translation.IndexOf(tag, StringComparison.OrdinalIgnoreCase); + if (idx0 < 0) idx0 = 0; + else idx0 += tag.Length; + + idx1 = translation.IndexOf("[i2p_", idx0 + 1, StringComparison.OrdinalIgnoreCase); + if (idx1 < 0) idx1 = translation.Length; + + translation = translation.Substring(idx0, idx1 - idx0); + } + } + + internal static string GetLocalizationParam(string ParamName, GameObject root) + { + string result = null; + if (root) + { + var components = root.GetComponents(); + for (int i = 0, imax = components.Length; i < imax; ++i) + { + var manager = components[i] as ILocalizationParamsManager; + if (manager != null && components[i].enabled) + { + result = manager.GetParameterValue(ParamName); + if (result != null) + return result; + } + } + } + + for (int i = 0, imax = ParamManagers.Count; i < imax; ++i) + { + result = ParamManagers[i].GetParameterValue(ParamName); + if (result != null) + return result; + } + + return null; + } + + #endregion + + #region Plural + + private static string GetPluralType( MatchCollection matches, string langCode, _GetParam getParam) + { + for (int i = 0, nMatches = matches.Count; i < nMatches; ++i) + { + var match = matches[i]; + var param = match.Groups[match.Groups.Count - 1].Value; + var result = (string)getParam(param); + if (result == null) + continue; + + int amount = 0; + if (!int.TryParse (result, out amount)) + continue; + + var pluralType = GoogleLanguages.GetPluralType(langCode, amount); + return pluralType.ToString (); + } + return null; + } + + #endregion + } +} diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Parameters.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Parameters.cs.meta new file mode 100644 index 00000000..703501dd --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Parameters.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 9bdcae51172bbb8458cf8c42f889c2a0 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_RTL.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_RTL.cs new file mode 100644 index 00000000..f84b9594 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_RTL.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; + +namespace TEngine.Localization +{ + public static partial class LocalizationManager + { + static string[] LanguagesRTL = {"ar-DZ", "ar","ar-BH","ar-EG","ar-IQ","ar-JO","ar-KW","ar-LB","ar-LY","ar-MA","ar-OM","ar-QA","ar-SA","ar-SY","ar-TN","ar-AE","ar-YE", + "fa", "he","ur","ji"}; + + public static string ApplyRTLfix(string line) { return ApplyRTLfix(line, 0, true); } + public static string ApplyRTLfix(string line, int maxCharacters, bool ignoreNumbers) + { + if (string.IsNullOrEmpty(line)) + return line; + + // Fix !, ? and . signs not set correctly + char firstC = line[0]; + if (firstC == '!' || firstC == '.' || firstC == '?') + line = line.Substring(1) + firstC; + + int tagStart = -1, tagEnd = 0; + + // Find all Tags (and Numbers if ignoreNumbers is true) + int tagBase = 0xFFFB; + tagEnd = 0; + var tags = new List(); + while (I2Utils.FindNextTag(line, tagEnd, out tagStart, out tagEnd)) + { + char tag = (char)(tagBase - tags.Count); + tags.Add(line.Substring(tagStart, tagEnd - tagStart + 1)); + + line = line.Substring(0, tagStart) + tag + line.Substring(tagEnd + 1); + tagEnd = tagStart + 1; + } + + // Split into lines and fix each line + line = line.Replace("\r\n", "\n"); + line = I2Utils.SplitLine(line, maxCharacters); + line = RTLFixer.Fix(line, true, !ignoreNumbers); + + + // Restore all tags + + for (int i = 0; i < tags.Count; i++) + { + string old_tag = ((char)(tagBase - i)).ToString(); + string new_tag = I2Utils.ReverseText(tags[i]); + + line = line.Replace(old_tag, new_tag); + } + + return line; + } + + + public static string FixRTL_IfNeeded(string text, int maxCharacters = 0, bool ignoreNumber=false) + { + if (IsRight2Left) + return ApplyRTLfix(text, maxCharacters, ignoreNumber); + return text; + } + + public static bool IsRTL(string Code) + { + return Array.IndexOf(LanguagesRTL, Code)>=0; + } + } + +} diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_RTL.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_RTL.cs.meta new file mode 100644 index 00000000..bd1ca3c3 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_RTL.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: d33b5082085040d44ab79b83d25bffee +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Sources.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Sources.cs new file mode 100644 index 00000000..f6dbd915 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Sources.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace TEngine.Localization +{ + public static partial class LocalizationManager + { + + #region Variables: Misc + + public static List Sources = new List(); + public static string[] GlobalSources = { "I2Languages" }; + + #endregion + + #region Sources + + public static bool UpdateSources() + { + // UnregisterDeletededSources(); + // RegisterSourceInResources(); + // RegisterSceneSources(); +#if UNITY_EDITOR + if (!I2Utils.IsPlaying()) + { + RegisterSourceInEditor(); + } +#endif + return Sources.Count>0; + } + + static void UnregisterDeletededSources() + { + // Delete sources that were part of another scene and not longer available + for (int i=Sources.Count-1; i>=0; --i) + if (Sources[i] == null) + RemoveSource( Sources[i] ); + } + +#if UNITY_EDITOR + public static void RegisterSourceInEditor() + { + var sourceAsset = GetEditorAsset(); + if (sourceAsset == null) return; + + if (sourceAsset && !Sources.Contains(sourceAsset.mSource)) + { + if (!sourceAsset.mSource.mIsGlobalSource) + sourceAsset.mSource.mIsGlobalSource = true; + sourceAsset.mSource.owner = sourceAsset; + AddSource(sourceAsset.mSource); + } + } + + private static LanguageSourceAsset m_LastLanguageSourceAsset; + + public static LanguageSourceAsset GetEditorAsset(bool force = false) + { + if (m_LastLanguageSourceAsset != null && !force) + { + return m_LastLanguageSourceAsset; + } + + Debug.Log("I2LocalizationManager 加载编辑器资源数据"); + var sourceAsset = UnityEditor.AssetDatabase.LoadAssetAtPath(DefaultLocalizationHelper.I2GlobalSourcesEditorPath); + if (sourceAsset == null) + { + Debug.LogError($"错误 没有找到编辑器下的资源 {DefaultLocalizationHelper.I2GlobalSourcesEditorPath}"); + return null; + } + + m_LastLanguageSourceAsset = sourceAsset; + return sourceAsset; + } + +#endif + + static void RegisterSceneSources() + { + LanguageSource[] sceneSources = (LanguageSource[])Resources.FindObjectsOfTypeAll( typeof(LanguageSource) ); + foreach (LanguageSource source in sceneSources) + if (!Sources.Contains(source.mSource)) + { + if (source.mSource.owner == null) + source.mSource.owner = source; + AddSource( source.mSource ); + } + } + + static void RegisterSourceInResources() + { + // Find the Source that its on the Resources Folder + foreach (string SourceName in GlobalSources) + { + LanguageSourceAsset sourceAsset = ResourceManager.pInstance.GetAsset(SourceName); + + if (sourceAsset && !Sources.Contains(sourceAsset.mSource)) + { + if (!sourceAsset.mSource.mIsGlobalSource) + sourceAsset.mSource.mIsGlobalSource = true; + sourceAsset.mSource.owner = sourceAsset; + AddSource(sourceAsset.mSource); + } + } + } + + public static Func Callback_AllowSyncFromGoogle = null; + static bool AllowSyncFromGoogle(LanguageSourceData Source) + { + if (Callback_AllowSyncFromGoogle == null) + return true; + return Callback_AllowSyncFromGoogle.Invoke(Source); + } + + internal static void AddSource ( LanguageSourceData Source ) + { + if (Sources.Contains (Source)) + return; + + Sources.Add( Source ); + + if (Source.HasGoogleSpreadsheet() && Source.GoogleUpdateFrequency != LanguageSourceData.eGoogleUpdateFrequency.Never && AllowSyncFromGoogle(Source)) + { + #if !UNITY_EDITOR + Source.Import_Google_FromCache(); + bool justCheck = false; + #else + bool justCheck=true; + #endif + if (Source.GoogleUpdateDelay > 0) + CoroutineManager.Start( Delayed_Import_Google(Source, Source.GoogleUpdateDelay, justCheck) ); + else + Source.Import_Google(false, justCheck); + } + + //if (force) + { + for (int i = 0; i < Source.mLanguages.Count; ++i) + Source.mLanguages[i].SetLoaded(true); + } + + if (Source.mDictionary.Count==0) + Source.UpdateDictionary(true); + } + + static IEnumerator Delayed_Import_Google ( LanguageSourceData source, float delay, bool justCheck ) + { + yield return new WaitForSeconds( delay ); + if (source != null) // handle cases where the source is already deleted + { + source.Import_Google(false, justCheck); + } + } + + internal static void RemoveSource (LanguageSourceData Source ) + { + //Debug.Log ("RemoveSource " + Source+" " + Source.GetInstanceID()); + Sources.Remove( Source ); + } + + public static bool IsGlobalSource( string SourceName ) + { + return Array.IndexOf(GlobalSources, SourceName)>=0; + } + + public static LanguageSourceData GetSourceContaining( string term, bool fallbackToFirst = true ) + { + if (!string.IsNullOrEmpty(term)) + { + for (int i=0, imax=Sources.Count; i0 ? Sources[0] : null; + } + + public static Object FindAsset (string value) + { + for (int i=0, imax=Sources.Count; i("getDefault"); + mCurrentDeviceLanguage = locale.Call("toString"); + //https://stackoverflow.com/questions/4212320/get-the-current-language-in-device + + + if (!string.IsNullOrEmpty(mCurrentDeviceLanguage)) + { + mCurrentDeviceLanguage = mCurrentDeviceLanguage.Replace('_', '-'); + mCurrentDeviceLanguage = GoogleLanguages.GetLanguageName(mCurrentDeviceLanguage, true, true); + if (!string.IsNullOrEmpty(mCurrentDeviceLanguage)) + return; + } + } + catch (System.Exception) + { + } + #endif + + mCurrentDeviceLanguage = Application.systemLanguage.ToString(); + if (mCurrentDeviceLanguage == "ChineseSimplified") mCurrentDeviceLanguage = "Chinese (Simplified)"; + if (mCurrentDeviceLanguage == "ChineseTraditional") mCurrentDeviceLanguage = "Chinese (Traditional)"; + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_SystemLanguage.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_SystemLanguage.cs.meta new file mode 100644 index 00000000..42447d5b --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_SystemLanguage.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: fd54e24a8c653f341a9540011ba06f01 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Targets.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Targets.cs new file mode 100644 index 00000000..64b0a357 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Targets.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; + +namespace TEngine.Localization +{ + public static partial class LocalizationManager + { + + #region Variables: Misc + + public static List mLocalizeTargets = new List(); + + #endregion + + public static void RegisterTarget( ILocalizeTargetDescriptor desc ) + { + if (mLocalizeTargets.FindIndex(x => x.Name == desc.Name) != -1) + return; + + for (int i = 0; i < mLocalizeTargets.Count; ++i) + { + if (mLocalizeTargets[i].Priority > desc.Priority) + { + mLocalizeTargets.Insert(i, desc); + return; + } + } + mLocalizeTargets.Add(desc); + } + } +} diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Targets.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Targets.cs.meta new file mode 100644 index 00000000..83dae7bf --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Targets.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 84ead5dc664fd394490f4874012ed099 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Translation.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Translation.cs new file mode 100644 index 00000000..26326745 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Manager/LocalizationManager_Translation.cs @@ -0,0 +1,225 @@ +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + public static partial class LocalizationManager + { + + #region Variables: Misc + + public delegate void OnLocalizeCallback(); + public static event OnLocalizeCallback OnLocalizeEvent; + + static bool mLocalizeIsScheduled; + static bool mLocalizeIsScheduledWithForcedValue; + + public static bool HighlightLocalizedTargets = false; + + + #endregion + + public static string GetTranslation(string Term, bool FixForRTL = true, int maxLineLengthForRTL = 0, bool ignoreRTLnumbers = true, bool applyParameters = false, GameObject localParametersRoot = null, string overrideLanguage = null, bool allowLocalizedParameters=true) + { + string Translation = null; + TryGetTranslation(Term, out Translation, FixForRTL, maxLineLengthForRTL, ignoreRTLnumbers, applyParameters, localParametersRoot, overrideLanguage, allowLocalizedParameters); + + return Translation; + } + public static string GetTermTranslation(string Term, bool FixForRTL = true, int maxLineLengthForRTL = 0, bool ignoreRTLnumbers = true, bool applyParameters = false, GameObject localParametersRoot = null, string overrideLanguage = null, bool allowLocalizedParameters=true) + { + return GetTranslation(Term, FixForRTL, maxLineLengthForRTL, ignoreRTLnumbers, applyParameters, localParametersRoot, overrideLanguage, allowLocalizedParameters); + } + + + public static bool TryGetTranslation(string Term, out string Translation, bool FixForRTL = true, int maxLineLengthForRTL = 0, bool ignoreRTLnumbers = true, bool applyParameters = false, GameObject localParametersRoot = null, string overrideLanguage = null, bool allowLocalizedParameters=true) + { + Translation = null; + if (string.IsNullOrEmpty(Term)) + return false; + + InitializeIfNeeded(); + + for (int i = 0, imax = Sources.Count; i < imax; ++i) + { + if (Sources[i].TryGetTranslation(Term, out Translation, overrideLanguage)) + { + if (applyParameters) + ApplyLocalizationParams(ref Translation, localParametersRoot, allowLocalizedParameters); + + if (IsRight2Left && FixForRTL) + Translation = ApplyRTLfix(Translation, maxLineLengthForRTL, ignoreRTLnumbers); + return true; + } + } + + return false; + } + + public static T GetTranslatedObject( string AssetName, Localize optionalLocComp=null) where T : Object + { + if (optionalLocComp != null) + { + return optionalLocComp.FindTranslatedObject(AssetName); + } + + T obj = FindAsset(AssetName) as T; + if (obj) + return obj; + + obj = ResourceManager.pInstance.GetAsset(AssetName); + return obj; + } + + public static T GetTranslatedObjectByTermName( string Term, Localize optionalLocComp=null) where T : Object + { + string translation = GetTranslation(Term, FixForRTL: false); + return GetTranslatedObject(translation); + } + + + public static string GetAppName(string languageCode) + { + if (!string.IsNullOrEmpty(languageCode)) + { + for (int i = 0; i < Sources.Count; ++i) + { + if (string.IsNullOrEmpty(Sources[i].mTerm_AppName)) + continue; + + int langIdx = Sources[i].GetLanguageIndexFromCode(languageCode, false); + if (langIdx < 0) + continue; + + var termData = Sources[i].GetTermData(Sources[i].mTerm_AppName); + if (termData == null) + continue; + + var appName = termData.GetTranslation(langIdx); + if (!string.IsNullOrEmpty(appName)) + return appName; + } + } + + return Application.productName; + } + + public static void LocalizeAll(bool Force = false) + { + LoadCurrentLanguage(); + + if (!Application.isPlaying) + { + DoLocalizeAll(Force); + return; + } + mLocalizeIsScheduledWithForcedValue |= Force; + if (mLocalizeIsScheduled) + { + return; + } + CoroutineManager.Start(Coroutine_LocalizeAll()); + } + + static IEnumerator Coroutine_LocalizeAll() + { + mLocalizeIsScheduled = true; + yield return null; + mLocalizeIsScheduled = false; + var force = mLocalizeIsScheduledWithForcedValue; + mLocalizeIsScheduledWithForcedValue = false; + DoLocalizeAll(force); + } + + static void DoLocalizeAll(bool Force = false) + { + Localize[] Locals = (Localize[])Resources.FindObjectsOfTypeAll( typeof(Localize) ); + for (int i=0, imax=Locals.Length; i GetCategories () + { + List Categories = new List (); + for (int i=0, imax=Sources.Count; i GetTermsList ( string Category = null ) + { + if (Sources.Count==0) + UpdateSources(); + + if (Sources.Count==1) + return Sources[0].GetTermsList(Category); + + HashSet Terms = new HashSet (); + for (int i=0, imax=Sources.Count; i(Terms); + } + + public static TermData GetTermData( string term ) + { + InitializeIfNeeded(); + + TermData data; + for (int i=0, imax=Sources.Count; i : ILocalizeTarget where T : Object + { + public T mTarget; + + public override bool IsValid(Localize cmp) + { + if (mTarget!=null) + { + var mTargetCmp = mTarget as Component; + if (mTargetCmp != null && mTargetCmp.gameObject != cmp.gameObject) + mTarget = null; + } + if (mTarget==null) + mTarget = cmp.GetComponent(); + return mTarget!=null; + } + } +} + diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/ILocalizeTarget.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/ILocalizeTarget.cs.meta new file mode 100644 index 00000000..4cd9e46c --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/ILocalizeTarget.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: bc110e805ac87134bb23eb4f9d6989e8 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/ILocalizeTargetDesc.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/ILocalizeTargetDesc.cs new file mode 100644 index 00000000..228f1a4d --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/ILocalizeTargetDesc.cs @@ -0,0 +1,41 @@ +using System; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace TEngine.Localization +{ + public abstract class ILocalizeTargetDescriptor + { + public string Name; + public int Priority; + public abstract bool CanLocalize(Localize cmp); + public abstract ILocalizeTarget CreateTarget(Localize cmp); + public abstract Type GetTargetType(); + } + + public abstract class LocalizeTargetDesc : ILocalizeTargetDescriptor where T : ILocalizeTarget + { + public override ILocalizeTarget CreateTarget(Localize cmp) { return ScriptableObject.CreateInstance(); } + public override Type GetTargetType() { return typeof(T); } + } + + + + public class LocalizeTargetDesc_Type : LocalizeTargetDesc where T: Object + where G: LocalizeTarget + { + public override bool CanLocalize(Localize cmp) { return cmp.GetComponent() != null; } + public override ILocalizeTarget CreateTarget(Localize cmp) + { + var target = cmp.GetComponent(); + if (target == null) + return null; + + var locTarget = ScriptableObject.CreateInstance(); + locTarget.mTarget = target; + return locTarget; + } + } + +} + diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/ILocalizeTargetDesc.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/ILocalizeTargetDesc.cs.meta new file mode 100644 index 00000000..651f1283 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/ILocalizeTargetDesc.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 65c3e6b4b14772c4e9fb35235497819b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_2DToolKit_Label.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_2DToolKit_Label.cs new file mode 100644 index 00000000..47e4aa94 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_2DToolKit_Label.cs @@ -0,0 +1,66 @@ +#if TK2D + +using UnityEngine; + +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [UnityEditor.InitializeOnLoad] + #endif + + public class LocalizeTarget_2DToolKit_Label : LocalizeTarget + { + static LocalizeTarget_2DToolKit_Label() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type() { Name = "2DToolKit Label", Priority = 100 }); } + + TextAnchor mOriginalAlignment = TextAnchor.MiddleCenter; + bool mInitializeAlignment = true; + + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.Text; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.TK2dFont; } + + public override bool CanUseSecondaryTerm() { return true; } + public override bool AllowMainTermToBeRTL() { return true; } + public override bool AllowSecondTermToBeRTL() { return false; } + + public override void GetFinalTerms(Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + primaryTerm = mTarget ? mTarget.text : null; + secondaryTerm = (mTarget.font != null ? mTarget.font.name : string.Empty); + } + + + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + //--[ Localize Font Object ]---------- + tk2dFont newFont = cmp.GetSecondaryTranslatedObj(ref mainTranslation, ref secondaryTranslation); + if (newFont != null && mTarget.font != newFont) + { + mTarget.font = newFont.data; + } + + if (mInitializeAlignment) + { + mInitializeAlignment = false; + mOriginalAlignment = mTarget.anchor; + } + + if (mainTranslation != null && mTarget.text != mainTranslation) + { + if (Localize.CurrentLocalizeComponent.CorrectAlignmentForRTL) + { + int align = (int)mTarget.anchor; + + if (align % 3 == 0) + mTarget.anchor = LocalizationManager.IsRight2Left ? mTarget.anchor + 2 : mOriginalAlignment; + else + if (align % 3 == 2) + mTarget.anchor = LocalizationManager.IsRight2Left ? mTarget.anchor - 2 : mOriginalAlignment; + } + mTarget.text = mainTranslation; + } + } + } +} +#endif + diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_2DToolKit_Label.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_2DToolKit_Label.cs.meta new file mode 100644 index 00000000..c8989caa --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_2DToolKit_Label.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: f7658f1522689aa4b93bc276acf537b1 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_2DToolKit_Sprite.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_2DToolKit_Sprite.cs new file mode 100644 index 00000000..4c924681 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_2DToolKit_Sprite.cs @@ -0,0 +1,53 @@ +#if TK2D + +using UnityEngine; + +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [UnityEditor.InitializeOnLoad] + #endif + + public class LocalizeTarget_2DToolKit_Sprite : LocalizeTarget + { + static LocalizeTarget_2DToolKit_Sprite() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type() { Name = "2DToolKit Sprite", Priority = 100 }); } + + + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.TK2dCollection; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.TK2dCollection; } + + public override bool CanUseSecondaryTerm() { return true; } + public override bool AllowMainTermToBeRTL() { return false; } + public override bool AllowSecondTermToBeRTL() { return false; } + + public override void GetFinalTerms(Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + primaryTerm = (mTarget.CurrentSprite != null ? mTarget.CurrentSprite.name : string.Empty); + secondaryTerm = (mTarget.Collection != null ? mTarget.Collection.spriteCollectionName : null); + } + + + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + if (string.IsNullOrEmpty(mainTranslation)) + return; + + //--[ Localize Atlas ]---------- + tk2dSpriteCollection newCollection = cmp.GetSecondaryTranslatedObj(ref mainTranslation, ref secondaryTranslation); + + if (newCollection != null) + { + if (mTarget.CurrentSprite.name != mainTranslation || mTarget.Collection.name != secondaryTranslation) + mTarget.SetSprite(newCollection.spriteCollection, mainTranslation); + } + else + { + if (mTarget.CurrentSprite.name != mainTranslation) + mTarget.SetSprite(mainTranslation); + } + } + } +} +#endif + diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_2DToolKit_Sprite.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_2DToolKit_Sprite.cs.meta new file mode 100644 index 00000000..f4ca51fc --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_2DToolKit_Sprite.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 53d5ed87a66514e439dddd19568adedd +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Label.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Label.cs new file mode 100644 index 00000000..53e92b2d --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Label.cs @@ -0,0 +1,94 @@ +#if NGUI + +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [InitializeOnLoad] + #endif + + public class LocalizeTarget_NGUI_Label : LocalizeTarget + { + static LocalizeTarget_NGUI_Label() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type { Name = "NGUI Label", Priority = 100 }); } + + NGUIText.Alignment mAlignment_RTL = NGUIText.Alignment.Right; + NGUIText.Alignment mAlignment_LTR = NGUIText.Alignment.Left; + bool mAlignmentWasRTL; + bool mInitializeAlignment = true; + + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.Text; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.UIFont; } + public override bool CanUseSecondaryTerm() { return true; } + public override bool AllowMainTermToBeRTL() { return true; } + public override bool AllowSecondTermToBeRTL() { return false; } + + public override void GetFinalTerms(Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + primaryTerm = mTarget ? mTarget.text : null; + secondaryTerm = mTarget.ambigiousFont != null ? mTarget.ambigiousFont.name : string.Empty; + } + + + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + //--[ Localize Font Object ]---------- + Font newFont = cmp.GetSecondaryTranslatedObj(ref mainTranslation, ref secondaryTranslation); + if (newFont != null) + { + if (newFont != mTarget.ambigiousFont) + mTarget.ambigiousFont = newFont; + } + if (newFont==null) + { + UIFont newUIFont = cmp.GetSecondaryTranslatedObj(ref mainTranslation, ref secondaryTranslation); + if (newUIFont != null && mTarget.ambigiousFont != newUIFont) + mTarget.ambigiousFont = newUIFont; + } + if (newFont == null) + { + NGUIFont newUIFont = cmp.GetSecondaryTranslatedObj(ref mainTranslation, ref secondaryTranslation); + if (newUIFont != null && mTarget.ambigiousFont != newUIFont) + mTarget.ambigiousFont = newUIFont; + } + + + if (mInitializeAlignment) + { + mInitializeAlignment = false; + mAlignment_LTR = mAlignment_RTL = mTarget.alignment; + + if (LocalizationManager.IsRight2Left && mAlignment_RTL == NGUIText.Alignment.Right) + mAlignment_LTR = NGUIText.Alignment.Left; + if (!LocalizationManager.IsRight2Left && mAlignment_LTR == NGUIText.Alignment.Left) + mAlignment_RTL = NGUIText.Alignment.Right; + } + + UIInput input = NGUITools.FindInParents(mTarget.gameObject); + if (input != null && input.label == mTarget) + { + if (mainTranslation != null && input.defaultText != mainTranslation) + { + if (cmp.CorrectAlignmentForRTL && (input.label.alignment == NGUIText.Alignment.Left || input.label.alignment == NGUIText.Alignment.Right)) + input.label.alignment = LocalizationManager.IsRight2Left ? mAlignment_RTL : mAlignment_LTR; + + input.defaultText = mainTranslation; + } + } + else + { + if (mainTranslation != null && mTarget.text != mainTranslation) + { + if (cmp.CorrectAlignmentForRTL && (mTarget.alignment == NGUIText.Alignment.Left || mTarget.alignment == NGUIText.Alignment.Right)) + mTarget.alignment = LocalizationManager.IsRight2Left ? mAlignment_RTL : mAlignment_LTR; + + mTarget.text = mainTranslation; + } + } + } + } +} +#endif + diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Label.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Label.cs.meta new file mode 100644 index 00000000..b1f87898 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Label.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 3bd97513069766847851e1041f64e378 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Sprite.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Sprite.cs new file mode 100644 index 00000000..3976e545 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Sprite.cs @@ -0,0 +1,65 @@ +#if NGUI + +using UnityEngine; + +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [UnityEditor.InitializeOnLoad] + #endif + + public class LocalizeTarget_NGUI_Sprite : LocalizeTarget + { + static LocalizeTarget_NGUI_Sprite() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type() { Name = "NGUI UISprite", Priority = 100 }); } + + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.Sprite; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.UIAtlas; } + public override bool CanUseSecondaryTerm () { return true; } + public override bool AllowMainTermToBeRTL () { return false; } + public override bool AllowSecondTermToBeRTL () { return false; } + + public override void GetFinalTerms ( Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm ) + { + primaryTerm = mTarget ? mTarget.spriteName : null; + secondaryTerm = (mTarget.atlas is UIAtlas atlas ? atlas.name : string.Empty); + } + + + public override void DoLocalize ( Localize cmp, string mainTranslation, string secondaryTranslation ) + { + if (mTarget.spriteName == mainTranslation) + return; + + //--[ Localize Atlas ]---------- + UIAtlas newAtlas = cmp.GetSecondaryTranslatedObj(ref mainTranslation, ref secondaryTranslation); + bool bChanged = false; + if (newAtlas != null && ((mTarget.atlas as UIAtlas) != newAtlas)) + { + mTarget.atlas = newAtlas; + bChanged = true; + } + + if (newAtlas==null) + { + NGUIAtlas newNGUIAtlas = cmp.GetSecondaryTranslatedObj(ref mainTranslation, ref secondaryTranslation); + if (newAtlas != null && ((mTarget.atlas as NGUIAtlas) != newNGUIAtlas)) + { + mTarget.atlas = newAtlas; + bChanged = true; + } + + } + + if (mTarget.spriteName != mainTranslation && mTarget.atlas.GetSprite(mainTranslation) != null) + { + mTarget.spriteName = mainTranslation; + bChanged = true; + } + if (bChanged) + mTarget.MakePixelPerfect(); + } + } +} +#endif + diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Sprite.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Sprite.cs.meta new file mode 100644 index 00000000..ad158713 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Sprite.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 545ef381b929ce44cad2bf111b096f2c +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Texture.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Texture.cs new file mode 100644 index 00000000..da86a995 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Texture.cs @@ -0,0 +1,41 @@ +#if NGUI + +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [InitializeOnLoad] + #endif + + public class LocalizeTarget_NGUI_Texture : LocalizeTarget + { + static LocalizeTarget_NGUI_Texture() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type { Name = "NGUI UITexture", Priority = 100 }); } + + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.Texture; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.Text; } + public override bool CanUseSecondaryTerm() { return false; } + public override bool AllowMainTermToBeRTL() { return false; } + public override bool AllowSecondTermToBeRTL() { return false; } + + public override void GetFinalTerms(Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + primaryTerm = mTarget!=null && mTarget.mainTexture!=null ? mTarget.mainTexture.name : null; + secondaryTerm = null; + } + + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + Texture Old = mTarget.mainTexture; + if (Old == null || Old.name != mainTranslation) + { + mTarget.mainTexture = cmp.FindTranslatedObject(mainTranslation); + mTarget.MakePixelPerfect(); + } + } + } +} +#endif + diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Texture.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Texture.cs.meta new file mode 100644 index 00000000..2e8fd038 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_NGUI_Texture.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 8488ebda4a4bc5343b6fcd2158af0360 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_SVGImporter_Image.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_SVGImporter_Image.cs new file mode 100644 index 00000000..afc37287 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_SVGImporter_Image.cs @@ -0,0 +1,42 @@ +#if SVG +using UnityEngine; + +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [UnityEditor.InitializeOnLoad] + #endif + + public class LocalizeTarget_SVGImporter_Image : LocalizeTarget + { + static LocalizeTarget_SVGImporter_Image() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type() { Name = "SVG Image", Priority = 100 }); } + + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.SVGAsset; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.Material; } + public override bool CanUseSecondaryTerm() { return true; } + public override bool AllowMainTermToBeRTL() { return false; } + public override bool AllowSecondTermToBeRTL() { return false; } + + public override void GetFinalTerms(Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + primaryTerm = (mTarget.vectorGraphics != null ? mTarget.vectorGraphics.name : string.Empty); + secondaryTerm = (mTarget.material != null ? mTarget.material.name : null); + } + + + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + var OldVectorG = mTarget.vectorGraphics; + if (OldVectorG == null || OldVectorG.name != mainTranslation) + mTarget.vectorGraphics = cmp.FindTranslatedObject(mainTranslation); + + var OldMaterial = mTarget.material; + if (OldMaterial == null || OldMaterial.name != secondaryTranslation) + mTarget.material = cmp.FindTranslatedObject(secondaryTranslation); + + mTarget.SetAllDirty(); + } + } +} +#endif diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_SVGImporter_Image.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_SVGImporter_Image.cs.meta new file mode 100644 index 00000000..0ad6478a --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_SVGImporter_Image.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: bbf97c698c733ea4bbf7bc2996984484 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_SVGImporter_Renderer.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_SVGImporter_Renderer.cs new file mode 100644 index 00000000..fdcf689a --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_SVGImporter_Renderer.cs @@ -0,0 +1,42 @@ +#if SVG +using UnityEngine; + +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [UnityEditor.InitializeOnLoad] + #endif + + public class LocalizeTarget_SVGImporter_Renderer : LocalizeTarget + { + static LocalizeTarget_SVGImporter_Renderer() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type() { Name = "SVG Renderer", Priority = 100 }); } + + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.SVGAsset; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.Material; } + public override bool CanUseSecondaryTerm() { return true; } + public override bool AllowMainTermToBeRTL() { return false; } + public override bool AllowSecondTermToBeRTL() { return false; } + + public override void GetFinalTerms(Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + primaryTerm = (mTarget.vectorGraphics != null ? mTarget.vectorGraphics.name : string.Empty); + secondaryTerm = (mTarget.opaqueMaterial != null ? mTarget.opaqueMaterial.name : string.Empty); + } + + + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + var OldVectorG = mTarget.vectorGraphics; + if (OldVectorG == null || OldVectorG.name != mainTranslation) + mTarget.vectorGraphics = cmp.FindTranslatedObject(mainTranslation); + + var OldMaterial = mTarget.opaqueMaterial; + if (OldMaterial == null || OldMaterial.name != secondaryTranslation) + mTarget.opaqueMaterial = cmp.FindTranslatedObject(secondaryTranslation); + + mTarget.SetAllDirty(); + } + } +} +#endif diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_SVGImporter_Renderer.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_SVGImporter_Renderer.cs.meta new file mode 100644 index 00000000..0ac2b0df --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_SVGImporter_Renderer.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: c78c473b19acc3844ba488904981d75c +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_TextMeshPro_Label.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_TextMeshPro_Label.cs new file mode 100644 index 00000000..e565bb57 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_TextMeshPro_Label.cs @@ -0,0 +1,191 @@ +using System; +using TMPro; +using UnityEditor; +using UnityEngine; + +#if TextMeshPro +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [InitializeOnLoad] + #endif + + public class LocalizeTarget_TextMeshPro_Label : LocalizeTarget + { + static LocalizeTarget_TextMeshPro_Label() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type { Name = "TextMeshPro Label", Priority = 100 }); } + + TextAlignmentOptions mAlignment_RTL = TextAlignmentOptions.Right; + TextAlignmentOptions mAlignment_LTR = TextAlignmentOptions.Left; + bool mAlignmentWasRTL; + bool mInitializeAlignment = true; + + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.Text; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.Font; } + public override bool CanUseSecondaryTerm() { return true; } + public override bool AllowMainTermToBeRTL() { return true; } + public override bool AllowSecondTermToBeRTL() { return false; } + + public override void GetFinalTerms ( Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + primaryTerm = mTarget ? mTarget.text : null; + secondaryTerm = mTarget.font != null ? mTarget.font.name : string.Empty; + } + + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + //--[ Localize Font Object ]---------- + { + TMP_FontAsset newFont = cmp.GetSecondaryTranslatedObj(ref mainTranslation, ref secondaryTranslation); + + if (newFont != null) + { + SetFont(mTarget, newFont); + } + else + { + //--[ Localize Font Material ]---------- + Material newMat = cmp.GetSecondaryTranslatedObj(ref mainTranslation, ref secondaryTranslation); + if (newMat != null && mTarget.fontMaterial != newMat) + { + if (!newMat.name.StartsWith(mTarget.font.name, StringComparison.Ordinal)) + { + newFont = GetTMPFontFromMaterial(cmp, secondaryTranslation.EndsWith(newMat.name, StringComparison.Ordinal) ? secondaryTranslation : newMat.name); + if (newFont != null) + SetFont(mTarget, newFont); + } + SetMaterial(mTarget, newMat); + } + + } + } + if (mInitializeAlignment) + { + mInitializeAlignment = false; + mAlignmentWasRTL = LocalizationManager.IsRight2Left; + InitAlignment_TMPro(mAlignmentWasRTL, mTarget.alignment, out mAlignment_LTR, out mAlignment_RTL); + } + else + { + TextAlignmentOptions alignRTL, alignLTR; + InitAlignment_TMPro(mAlignmentWasRTL, mTarget.alignment, out alignLTR, out alignRTL); + + if (mAlignmentWasRTL && mAlignment_RTL != alignRTL || + !mAlignmentWasRTL && mAlignment_LTR != alignLTR) + { + mAlignment_LTR = alignLTR; + mAlignment_RTL = alignRTL; + } + mAlignmentWasRTL = LocalizationManager.IsRight2Left; + } + + if (mainTranslation != null && mTarget.text != mainTranslation) + { + if (cmp.CorrectAlignmentForRTL) + { + mTarget.alignment = LocalizationManager.IsRight2Left ? mAlignment_RTL : mAlignment_LTR; + } + + mTarget.isRightToLeftText = LocalizationManager.IsRight2Left; + if (LocalizationManager.IsRight2Left) mainTranslation = I2Utils.ReverseText(mainTranslation); + + mTarget.text = mainTranslation; + } + } + + #region Tools + internal static TMP_FontAsset GetTMPFontFromMaterial(Localize cmp, string matName) + { + string splitChars = " .\\/-[]()"; + for (int i = matName.Length - 1; i > 0;) + { + // Find first valid character + while (i > 0 && splitChars.IndexOf(matName[i]) >= 0) + i--; + + if (i <= 0) break; + + var fontName = matName.Substring(0, i + 1); + var obj = cmp.GetObject(fontName); + if (obj != null) + return obj; + + // skip this word + while (i > 0 && splitChars.IndexOf(matName[i]) < 0) + i--; + } + + return null; + } + + internal static void InitAlignment_TMPro(bool isRTL, TextAlignmentOptions alignment, out TextAlignmentOptions alignLTR, out TextAlignmentOptions alignRTL) + { + alignLTR = alignRTL = alignment; + + if (isRTL) + { + switch (alignment) + { + case TextAlignmentOptions.TopRight: alignLTR = TextAlignmentOptions.TopLeft; break; + case TextAlignmentOptions.Right: alignLTR = TextAlignmentOptions.Left; break; + case TextAlignmentOptions.BottomRight: alignLTR = TextAlignmentOptions.BottomLeft; break; + case TextAlignmentOptions.BaselineRight: alignLTR = TextAlignmentOptions.BaselineLeft; break; + case TextAlignmentOptions.MidlineRight: alignLTR = TextAlignmentOptions.MidlineLeft; break; + case TextAlignmentOptions.CaplineRight: alignLTR = TextAlignmentOptions.CaplineLeft; break; + + case TextAlignmentOptions.TopLeft: alignLTR = TextAlignmentOptions.TopRight; break; + case TextAlignmentOptions.Left: alignLTR = TextAlignmentOptions.Right; break; + case TextAlignmentOptions.BottomLeft: alignLTR = TextAlignmentOptions.BottomRight; break; + case TextAlignmentOptions.BaselineLeft: alignLTR = TextAlignmentOptions.BaselineRight; break; + case TextAlignmentOptions.MidlineLeft: alignLTR = TextAlignmentOptions.MidlineRight; break; + case TextAlignmentOptions.CaplineLeft: alignLTR = TextAlignmentOptions.CaplineRight; break; + + } + } + else + { + switch (alignment) + { + case TextAlignmentOptions.TopRight: alignRTL = TextAlignmentOptions.TopLeft; break; + case TextAlignmentOptions.Right: alignRTL = TextAlignmentOptions.Left; break; + case TextAlignmentOptions.BottomRight: alignRTL = TextAlignmentOptions.BottomLeft; break; + case TextAlignmentOptions.BaselineRight: alignRTL = TextAlignmentOptions.BaselineLeft; break; + case TextAlignmentOptions.MidlineRight: alignRTL = TextAlignmentOptions.MidlineLeft; break; + case TextAlignmentOptions.CaplineRight: alignRTL = TextAlignmentOptions.CaplineLeft; break; + + case TextAlignmentOptions.TopLeft: alignRTL = TextAlignmentOptions.TopRight; break; + case TextAlignmentOptions.Left: alignRTL = TextAlignmentOptions.Right; break; + case TextAlignmentOptions.BottomLeft: alignRTL = TextAlignmentOptions.BottomRight; break; + case TextAlignmentOptions.BaselineLeft: alignRTL = TextAlignmentOptions.BaselineRight; break; + case TextAlignmentOptions.MidlineLeft: alignRTL = TextAlignmentOptions.MidlineRight; break; + case TextAlignmentOptions.CaplineLeft: alignRTL = TextAlignmentOptions.CaplineRight; break; + } + } + } + + internal static void SetFont(TMP_Text label, TMP_FontAsset newFont) + { + if (label.font != newFont) + { + label.font = newFont; + } + if (label.linkedTextComponent != null) + { + SetFont(label.linkedTextComponent, newFont); + } + } + internal static void SetMaterial(TMP_Text label, Material newMat) + { + if (label.fontSharedMaterial != newMat) + { + label.fontSharedMaterial = newMat; + } + if (label.linkedTextComponent != null) + { + SetMaterial(label.linkedTextComponent, newMat); + } + } + #endregion + } +} +#endif \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_TextMeshPro_Label.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_TextMeshPro_Label.cs.meta new file mode 100644 index 00000000..f9e954b5 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_TextMeshPro_Label.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: d96a6c3ea7d28744aacc367115a71af5 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_TextMeshPro_UGUI.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_TextMeshPro_UGUI.cs new file mode 100644 index 00000000..2279b028 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_TextMeshPro_UGUI.cs @@ -0,0 +1,98 @@ +using System; +using TMPro; +using UnityEditor; +using UnityEngine; + +#if TextMeshPro +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [InitializeOnLoad] + #endif + + public class LocalizeTarget_TextMeshPro_UGUI : LocalizeTarget + { + static LocalizeTarget_TextMeshPro_UGUI() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type { Name = "TextMeshPro UGUI", Priority = 100 }); } + + public TextAlignmentOptions mAlignment_RTL = TextAlignmentOptions.Right; + public TextAlignmentOptions mAlignment_LTR = TextAlignmentOptions.Left; + public bool mAlignmentWasRTL; + public bool mInitializeAlignment = true; + + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.Text; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.TextMeshPFont; } + public override bool CanUseSecondaryTerm() { return true; } + public override bool AllowMainTermToBeRTL() { return true; } + public override bool AllowSecondTermToBeRTL() { return false; } + + public override void GetFinalTerms ( Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + primaryTerm = mTarget ? mTarget.text : null; + secondaryTerm = mTarget.font != null ? mTarget.font.name : string.Empty; + } + + + + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + { + //--[ Localize Font Object ]---------- + TMP_FontAsset newFont = cmp.GetSecondaryTranslatedObj(ref mainTranslation, ref secondaryTranslation); + + if (newFont != null) + { + LocalizeTarget_TextMeshPro_Label.SetFont(mTarget, newFont); + } + else + { + //--[ Localize Font Material ]---------- + Material newMat = cmp.GetSecondaryTranslatedObj(ref mainTranslation, ref secondaryTranslation); + if (newMat != null && mTarget.fontMaterial != newMat) + { + if (!newMat.name.StartsWith(mTarget.font.name, StringComparison.Ordinal)) + { + newFont = LocalizeTarget_TextMeshPro_Label.GetTMPFontFromMaterial(cmp, secondaryTranslation.EndsWith(newMat.name, StringComparison.Ordinal) ? secondaryTranslation : newMat.name); + if (newFont != null) + LocalizeTarget_TextMeshPro_Label.SetFont(mTarget, newFont); + } + LocalizeTarget_TextMeshPro_Label.SetMaterial( mTarget, newMat ); + } + } + } + + if (mInitializeAlignment) + { + mInitializeAlignment = false; + mAlignmentWasRTL = LocalizationManager.IsRight2Left; + LocalizeTarget_TextMeshPro_Label.InitAlignment_TMPro(mAlignmentWasRTL, mTarget.alignment, out mAlignment_LTR, out mAlignment_RTL); + } + else + { + TextAlignmentOptions alignRTL, alignLTR; + LocalizeTarget_TextMeshPro_Label.InitAlignment_TMPro(mAlignmentWasRTL, mTarget.alignment, out alignLTR, out alignRTL); + + if (mAlignmentWasRTL && mAlignment_RTL != alignRTL || + !mAlignmentWasRTL && mAlignment_LTR != alignLTR) + { + mAlignment_LTR = alignLTR; + mAlignment_RTL = alignRTL; + } + mAlignmentWasRTL = LocalizationManager.IsRight2Left; + } + + if (mainTranslation != null && mTarget.text != mainTranslation) + { + if (cmp.CorrectAlignmentForRTL) + { + mTarget.alignment = LocalizationManager.IsRight2Left ? mAlignment_RTL : mAlignment_LTR; + } + if (LocalizationManager.IsRight2Left) mainTranslation = I2Utils.ReverseText(mainTranslation); + + mTarget.isRightToLeftText = LocalizationManager.IsRight2Left; + mTarget.text = mainTranslation; + } + } + } +} +#endif \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_TextMeshPro_UGUI.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_TextMeshPro_UGUI.cs.meta new file mode 100644 index 00000000..acb45b3f --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_TextMeshPro_UGUI.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 481ab606793a67349be805c13febeba0 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_AudioSource.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_AudioSource.cs new file mode 100644 index 00000000..86ddd2f2 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_AudioSource.cs @@ -0,0 +1,45 @@ +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [InitializeOnLoad] + #endif + + public class LocalizeTarget_UnityStandard_AudioSource : LocalizeTarget + { + static LocalizeTarget_UnityStandard_AudioSource() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type { Name = "AudioSource", Priority = 100 }); } + + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.AudioClip; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.Text; } + public override bool CanUseSecondaryTerm() { return false; } + public override bool AllowMainTermToBeRTL() { return false; } + public override bool AllowSecondTermToBeRTL() { return false; } + + public override void GetFinalTerms ( Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + AudioClip clip = mTarget.clip; + primaryTerm = clip ? clip.name : string.Empty; + secondaryTerm = null; + } + + + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + bool bIsPlaying = (mTarget.isPlaying || mTarget.loop) && Application.isPlaying; + AudioClip OldClip = mTarget.clip; + AudioClip NewClip = cmp.FindTranslatedObject(mainTranslation); + if (OldClip != NewClip) + mTarget.clip = NewClip; + + if (bIsPlaying && mTarget.clip) + mTarget.Play(); + + // If the old clip is not in the translatedObjects, then unload it as it most likely was loaded from Resources + //if (!HasTranslatedObject(OldClip)) + // Resources.UnloadAsset(OldClip); + } + } +} diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_AudioSource.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_AudioSource.cs.meta new file mode 100644 index 00000000..6f903d82 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_AudioSource.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 007f2c3f7b0e4a048ae89d65dcd38729 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_Child.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_Child.cs new file mode 100644 index 00000000..c3f25c11 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_Child.cs @@ -0,0 +1,51 @@ +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + public class LocalizeTargetDesc_Child : LocalizeTargetDesc + { + public override bool CanLocalize(Localize cmp) { return cmp.transform.childCount > 1; } + } + + #if UNITY_EDITOR + [InitializeOnLoad] + #endif + + public class LocalizeTarget_UnityStandard_Child : LocalizeTarget + { + static LocalizeTarget_UnityStandard_Child() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Child { Name = "Child", Priority = 200 }); } + + public override bool IsValid(Localize cmp) { return cmp.transform.childCount>1; } + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.GameObject; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.Text; } + public override bool CanUseSecondaryTerm() { return false; } + public override bool AllowMainTermToBeRTL() { return false; } + public override bool AllowSecondTermToBeRTL() { return false; } + + public override void GetFinalTerms(Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + primaryTerm = cmp.name; + secondaryTerm = null; + } + + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + if (string.IsNullOrEmpty(mainTranslation)) + return; + Transform locTr = cmp.transform; + + var objName = mainTranslation; + var idx = mainTranslation.LastIndexOfAny(LanguageSourceData.CategorySeparators); + if (idx >= 0) + objName = objName.Substring(idx + 1); + + for (int i = 0; i < locTr.childCount; ++i) + { + var child = locTr.GetChild(i); + child.gameObject.SetActive(child.name == objName); + } + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_Child.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_Child.cs.meta new file mode 100644 index 00000000..2ef5fae1 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_Child.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 61a67a81d07fe85429a253a86ebac910 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_MeshRenderer.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_MeshRenderer.cs new file mode 100644 index 00000000..1643939e --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_MeshRenderer.cs @@ -0,0 +1,80 @@ +using UnityEditor; +using UnityEngine; + +#pragma warning disable 618 + +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [InitializeOnLoad] + #endif + + public class LocalizeTarget_UnityStandard_MeshRenderer : LocalizeTarget + { + static LocalizeTarget_UnityStandard_MeshRenderer() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type { Name = "MeshRenderer", Priority = 800 }); } + + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.Mesh; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.Material; } + public override bool CanUseSecondaryTerm() { return true; } + public override bool AllowMainTermToBeRTL() { return false; } + public override bool AllowSecondTermToBeRTL() { return false; } + + public override void GetFinalTerms ( Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + if (mTarget==null) + { + primaryTerm = secondaryTerm = null; + } + else + { + MeshFilter filter = mTarget.GetComponent(); + if (filter==null || filter.sharedMesh==null) + { + primaryTerm = null; + } + else + { + #if UNITY_EDITOR + primaryTerm = AssetDatabase.GetAssetPath(filter.sharedMesh); + I2Utils.RemoveResourcesPath(ref primaryTerm); + #else + primaryTerm = filter.sharedMesh.name; + #endif + } + } + + if (mTarget==null || mTarget.sharedMaterial==null) + { + secondaryTerm = null; + } + else + { + #if UNITY_EDITOR + secondaryTerm = AssetDatabase.GetAssetPath(mTarget.sharedMaterial); + I2Utils.RemoveResourcesPath(ref secondaryTerm); + #else + secondaryTerm = mTarget.sharedMaterial.name; + #endif + } + } + + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + //--[ Localize Material]---------- + Material newMat = cmp.GetSecondaryTranslatedObj(ref mainTranslation, ref secondaryTranslation); + if (newMat != null && mTarget.sharedMaterial != newMat) + { + mTarget.material = newMat; + } + + //--[ Localize Mesh ]---------- + Mesh newMesh = cmp.FindTranslatedObject( mainTranslation); + MeshFilter filter = mTarget.GetComponent(); + if (newMesh != null && filter.sharedMesh != newMesh) + { + filter.mesh = newMesh; + } + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_MeshRenderer.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_MeshRenderer.cs.meta new file mode 100644 index 00000000..883f5974 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_MeshRenderer.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: b4679336707a2b54caa10d99561be751 +timeCreated: 1518408606 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_Prefab.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_Prefab.cs new file mode 100644 index 00000000..9022a557 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_Prefab.cs @@ -0,0 +1,96 @@ +using UnityEditor; +using UnityEngine; + +#pragma warning disable 618 + +namespace TEngine.Localization +{ + public class LocalizeTargetDesc_Prefab : LocalizeTargetDesc + { + public override bool CanLocalize(Localize cmp) { return true; } + } + + #if UNITY_EDITOR + [InitializeOnLoad] + #endif + + public class LocalizeTarget_UnityStandard_Prefab : LocalizeTarget + { + static LocalizeTarget_UnityStandard_Prefab() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Prefab { Name = "Prefab", Priority = 250 }); } + + public override bool IsValid(Localize cmp) { return true; } + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.GameObject; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.Text; } + public override bool CanUseSecondaryTerm() { return false; } + public override bool AllowMainTermToBeRTL() { return false; } + public override bool AllowSecondTermToBeRTL() { return false; } + + public override void GetFinalTerms ( Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + primaryTerm = cmp.name; + secondaryTerm = null; + } + + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + if (string.IsNullOrEmpty(mainTranslation)) + return; + + if (mTarget && mTarget.name == mainTranslation) + return; + + Transform locTr = cmp.transform; + + var objName = mainTranslation; + var idx = mainTranslation.LastIndexOfAny(LanguageSourceData.CategorySeparators); + if (idx >= 0) + objName = objName.Substring(idx + 1); + + Transform mNew = InstantiateNewPrefab(cmp, mainTranslation); + if (mNew == null) + return; + mNew.name = objName; + + for (int i = locTr.childCount - 1; i >= 0; --i) + { + var child = locTr.GetChild(i); + if (child!=mNew) + { + #if UNITY_EDITOR + if (Application.isPlaying) + Destroy(child.gameObject); + else + DestroyImmediate(child.gameObject); + #else + Object.Destroy (child.gameObject); + #endif + } + } + } + + Transform InstantiateNewPrefab(Localize cmp, string mainTranslation) + { + GameObject NewPrefab = cmp.FindTranslatedObject(mainTranslation); + if (NewPrefab == null) + return null; + + GameObject current = mTarget; + + mTarget = Instantiate(NewPrefab); + if (mTarget == null) + return null; + + Transform locTr = cmp.transform; + Transform mNew = mTarget.transform; + mNew.SetParent(locTr); + + Transform bBase = current ? current.transform : locTr; + //mNew.localScale = bBase.localScale; + mNew.rotation = bBase.rotation; + mNew.position = bBase.position; + + return mNew; + } + } +} diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_Prefab.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_Prefab.cs.meta new file mode 100644 index 00000000..6b0935dd --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_Prefab.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: bd8b481d182cbcd4293524eb92ee520c +timeCreated: 1518408606 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_SpriteRenderer.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_SpriteRenderer.cs new file mode 100644 index 00000000..c4d849a0 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_SpriteRenderer.cs @@ -0,0 +1,41 @@ +using UnityEditor; +using UnityEngine; + +#pragma warning disable 618 + +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [InitializeOnLoad] + #endif + + public class LocalizeTarget_UnityStandard_SpriteRenderer : LocalizeTarget + { + static LocalizeTarget_UnityStandard_SpriteRenderer() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type { Name = "SpriteRenderer", Priority = 100 }); } + + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.Sprite; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.Text; } + public override bool CanUseSecondaryTerm() { return false; } + public override bool AllowMainTermToBeRTL() { return false; } + public override bool AllowSecondTermToBeRTL() { return false; } + + public override void GetFinalTerms ( Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + Sprite sprite = mTarget.sprite; + primaryTerm = sprite != null ? sprite.name : string.Empty; + secondaryTerm = null; + } + + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + Sprite Old = mTarget.sprite; + if (Old == null || Old.name != mainTranslation) + mTarget.sprite = cmp.FindTranslatedObject(mainTranslation); + + // If the old value is not in the translatedObjects, then unload it as it most likely was loaded from Resources + //if (!HasTranslatedObject(Old)) + // Resources.UnloadAsset(Old); + } + } +} diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_SpriteRenderer.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_SpriteRenderer.cs.meta new file mode 100644 index 00000000..05bacef1 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_SpriteRenderer.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 3b29d7ab09a96634a9f704e6a1f21193 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_TextMesh.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_TextMesh.cs new file mode 100644 index 00000000..d13350ae --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_TextMesh.cs @@ -0,0 +1,68 @@ +using UnityEditor; +using UnityEngine; + +#pragma warning disable 618 + +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [InitializeOnLoad] + #endif + + public class LocalizeTarget_UnityStandard_TextMesh : LocalizeTarget + { + static LocalizeTarget_UnityStandard_TextMesh() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type { Name = "TextMesh", Priority = 100 }); } + + TextAlignment mAlignment_RTL = TextAlignment.Right; + TextAlignment mAlignment_LTR = TextAlignment.Left; + bool mAlignmentWasRTL; + bool mInitializeAlignment = true; + + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.Text; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.Font; } + public override bool CanUseSecondaryTerm() { return true; } + public override bool AllowMainTermToBeRTL() { return true; } + public override bool AllowSecondTermToBeRTL() { return false; } + + public override void GetFinalTerms ( Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + primaryTerm = mTarget ? mTarget.text : null; + secondaryTerm = string.IsNullOrEmpty(Secondary) && mTarget.font != null ? mTarget.font.name : null; + } + + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + //--[ Localize Font Object ]---------- + Font newFont = cmp.GetSecondaryTranslatedObj(ref mainTranslation, ref secondaryTranslation); + if (newFont != null && mTarget.font != newFont) + { + mTarget.font = newFont; + MeshRenderer rend = mTarget.GetComponentInChildren(); + rend.material = newFont.material; + } + + //--[ Localize Text ]---------- + if (mInitializeAlignment) + { + mInitializeAlignment = false; + + mAlignment_LTR = mAlignment_RTL = mTarget.alignment; + + if (LocalizationManager.IsRight2Left && mAlignment_RTL == TextAlignment.Right) + mAlignment_LTR = TextAlignment.Left; + if (!LocalizationManager.IsRight2Left && mAlignment_LTR == TextAlignment.Left) + mAlignment_RTL = TextAlignment.Right; + + } + if (mainTranslation != null && mTarget.text != mainTranslation) + { + if (cmp.CorrectAlignmentForRTL && mTarget.alignment != TextAlignment.Center) + mTarget.alignment = LocalizationManager.IsRight2Left ? mAlignment_RTL : mAlignment_LTR; + + mTarget.font.RequestCharactersInTexture(mainTranslation); + mTarget.text = mainTranslation; + } + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_TextMesh.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_TextMesh.cs.meta new file mode 100644 index 00000000..58ed8e6e --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_TextMesh.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 62e865dd37373234c9d966bdd78278e1 +timeCreated: 1518408606 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_VideoPlayer.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_VideoPlayer.cs new file mode 100644 index 00000000..42f813cb --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_VideoPlayer.cs @@ -0,0 +1,33 @@ +using UnityEditor; +using UnityEngine; +using UnityEngine.Video; + +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [InitializeOnLoad] + #endif + public class LocalizeTarget_UnityStandard_VideoPlayer : LocalizeTarget + { + static LocalizeTarget_UnityStandard_VideoPlayer() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type { Name = "VideoPlayer", Priority = 100 }); } + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.Video; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.Text; } + public override bool CanUseSecondaryTerm() { return false; } + public override bool AllowMainTermToBeRTL() { return false; } + public override bool AllowSecondTermToBeRTL() { return false; } + public override void GetFinalTerms ( Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + VideoClip clip = mTarget.clip; + primaryTerm = clip != null ? clip.name: string.Empty; + secondaryTerm = null; + } + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + VideoClip Old = mTarget.clip; + if (Old == null || Old.name != mainTranslation) + mTarget.clip = cmp.FindTranslatedObject(mainTranslation); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_VideoPlayer.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_VideoPlayer.cs.meta new file mode 100644 index 00000000..3d496e8a --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityStandard_VideoPlayer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 5fcaa99c1874460eb953851cbf4bfad2 +timeCreated: 1601759602 \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_Image.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_Image.cs new file mode 100644 index 00000000..fa26f732 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_Image.cs @@ -0,0 +1,53 @@ +using UnityEditor; +using UnityEngine; +using UnityEngine.UI; + +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [InitializeOnLoad] + #endif + + public class LocalizeTarget_UnityUI_Image : LocalizeTarget + { + static LocalizeTarget_UnityUI_Image() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type { Name = "Image", Priority = 100 }); } + + public override bool CanUseSecondaryTerm () { return false; } + public override bool AllowMainTermToBeRTL () { return false; } + public override bool AllowSecondTermToBeRTL () { return false; } + public override eTermType GetPrimaryTermType(Localize cmp) + { + return mTarget.sprite == null ? eTermType.Texture : eTermType.Sprite; + } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.Text; } + + + public override void GetFinalTerms ( Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm ) + { + primaryTerm = mTarget.mainTexture ? mTarget.mainTexture.name : ""; + if (mTarget.sprite!=null && mTarget.sprite.name!=primaryTerm) + primaryTerm += "." + mTarget.sprite.name; + + secondaryTerm = null; + } + + + public override void DoLocalize ( Localize cmp, string mainTranslation, string secondaryTranslation ) + { + Sprite Old = mTarget.sprite; + if (Old==null || Old.name!=mainTranslation) + mTarget.sprite = cmp.FindTranslatedObject( mainTranslation ); + + // If the old value is not in the translatedObjects, then unload it as it most likely was loaded from Resources + //if (!HasTranslatedObject(Old)) + // Resources.UnloadAsset(Old); + + // In the editor, sometimes unity "forgets" to show the changes +#if UNITY_EDITOR + if (!Application.isPlaying) + EditorUtility.SetDirty( mTarget ); +#endif + } + } +} diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_Image.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_Image.cs.meta new file mode 100644 index 00000000..3910dd9f --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_Image.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: bedef2aeaac8da04faa9a07b7241d0ad +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_RawImage.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_RawImage.cs new file mode 100644 index 00000000..a611a391 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_RawImage.cs @@ -0,0 +1,47 @@ +using UnityEditor; +using UnityEngine; +using UnityEngine.UI; + +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [InitializeOnLoad] + #endif + + public class LocalizeTarget_UnityUI_RawImage : LocalizeTarget + { + static LocalizeTarget_UnityUI_RawImage() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type { Name = "RawImage", Priority = 100 }); } + + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.Texture; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.Text; } + public override bool CanUseSecondaryTerm() { return false; } + public override bool AllowMainTermToBeRTL() { return false; } + public override bool AllowSecondTermToBeRTL() { return false; } + + + public override void GetFinalTerms(Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm) + { + primaryTerm = mTarget.mainTexture ? mTarget.mainTexture.name : ""; + secondaryTerm = null; + } + + + public override void DoLocalize(Localize cmp, string mainTranslation, string secondaryTranslation) + { + Texture Old = mTarget.texture; + if (Old == null || Old.name != mainTranslation) + mTarget.texture = cmp.FindTranslatedObject(mainTranslation); + + // If the old value is not in the translatedObjects, then unload it as it most likely was loaded from Resources + //if (!HasTranslatedObject(Old)) + // Resources.UnloadAsset(Old); + + // In the editor, sometimes unity "forgets" to show the changes + #if UNITY_EDITOR + if (!Application.isPlaying) + EditorUtility.SetDirty(mTarget); + #endif + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_RawImage.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_RawImage.cs.meta new file mode 100644 index 00000000..6a4c5b0a --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_RawImage.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: a58a0cb6f0764ca42b2877aa2c6fa0af +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_Text.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_Text.cs new file mode 100644 index 00000000..cb5f1d7d --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_Text.cs @@ -0,0 +1,111 @@ +using UnityEditor; +using UnityEngine; +using UnityEngine.UI; + +namespace TEngine.Localization +{ + #if UNITY_EDITOR + [InitializeOnLoad] + #endif + + public class LocalizeTarget_UnityUI_Text : LocalizeTarget + { + static LocalizeTarget_UnityUI_Text() { AutoRegister(); } + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] static void AutoRegister() { LocalizationManager.RegisterTarget(new LocalizeTargetDesc_Type { Name = "Text", Priority = 100 }); } + + TextAnchor mAlignment_RTL = TextAnchor.UpperRight; + TextAnchor mAlignment_LTR = TextAnchor.UpperLeft; + bool mAlignmentWasRTL; + bool mInitializeAlignment = true; + + public override eTermType GetPrimaryTermType(Localize cmp) { return eTermType.Text; } + public override eTermType GetSecondaryTermType(Localize cmp) { return eTermType.Font; } + public override bool CanUseSecondaryTerm () { return true; } + public override bool AllowMainTermToBeRTL () { return true; } + public override bool AllowSecondTermToBeRTL () { return false; } + + public override void GetFinalTerms ( Localize cmp, string Main, string Secondary, out string primaryTerm, out string secondaryTerm ) + { + primaryTerm = mTarget ? mTarget.text : null; + secondaryTerm = mTarget.font!=null ? mTarget.font.name : string.Empty; + } + + + public override void DoLocalize ( Localize cmp, string mainTranslation, string secondaryTranslation ) + { + //--[ Localize Font Object ]---------- + Font newFont = cmp.GetSecondaryTranslatedObj( ref mainTranslation, ref secondaryTranslation ); + if (newFont!=null && newFont!=mTarget.font) + mTarget.font = newFont; + + if (mInitializeAlignment) + { + mInitializeAlignment = false; + mAlignmentWasRTL = LocalizationManager.IsRight2Left; + InitAlignment( mAlignmentWasRTL, mTarget.alignment, out mAlignment_LTR, out mAlignment_RTL ); + } + else + { + TextAnchor alignRTL, alignLTR; + InitAlignment( mAlignmentWasRTL, mTarget.alignment, out alignLTR, out alignRTL ); + + if (mAlignmentWasRTL && mAlignment_RTL!=alignRTL || + !mAlignmentWasRTL && mAlignment_LTR != alignLTR) + { + mAlignment_LTR = alignLTR; + mAlignment_RTL = alignRTL; + } + mAlignmentWasRTL = LocalizationManager.IsRight2Left; + } + + if (mainTranslation!=null && mTarget.text != mainTranslation) + { + if (cmp.CorrectAlignmentForRTL) + { + mTarget.alignment = LocalizationManager.IsRight2Left ? mAlignment_RTL : mAlignment_LTR; + } + + + mTarget.text = mainTranslation; + mTarget.SetVerticesDirty(); + + // In the editor, sometimes unity "forgets" to show the changes + #if UNITY_EDITOR + if (!Application.isPlaying) + EditorUtility.SetDirty( mTarget ); + #endif + } + } + + void InitAlignment ( bool isRTL, TextAnchor alignment, out TextAnchor alignLTR, out TextAnchor alignRTL ) + { + alignLTR = alignRTL = alignment; + + if (isRTL) + { + switch (alignment) + { + case TextAnchor.UpperRight: alignLTR = TextAnchor.UpperLeft; break; + case TextAnchor.MiddleRight: alignLTR = TextAnchor.MiddleLeft; break; + case TextAnchor.LowerRight: alignLTR = TextAnchor.LowerLeft; break; + case TextAnchor.UpperLeft: alignLTR = TextAnchor.UpperRight; break; + case TextAnchor.MiddleLeft: alignLTR = TextAnchor.MiddleRight; break; + case TextAnchor.LowerLeft: alignLTR = TextAnchor.LowerRight; break; + } + } + else + { + switch (alignment) + { + case TextAnchor.UpperRight: alignRTL = TextAnchor.UpperLeft; break; + case TextAnchor.MiddleRight: alignRTL = TextAnchor.MiddleLeft; break; + case TextAnchor.LowerRight: alignRTL = TextAnchor.LowerLeft; break; + case TextAnchor.UpperLeft: alignRTL = TextAnchor.UpperRight; break; + case TextAnchor.MiddleLeft: alignRTL = TextAnchor.MiddleRight; break; + case TextAnchor.LowerLeft: alignRTL = TextAnchor.LowerRight; break; + } + } + } + } +} + diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_Text.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_Text.cs.meta new file mode 100644 index 00000000..2372f6e8 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Targets/LocalizeTarget_UnityUI_Text.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 332e36893e7cf4a49b3c1f72f76cd5e1 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/TermData.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/TermData.cs new file mode 100644 index 00000000..21fa17d9 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/TermData.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace TEngine.Localization +{ + public enum eTermType + { + Text, Font, Texture, AudioClip, GameObject, Sprite, Material, Child, Mesh, + #if NGUI + UIAtlas, UIFont, + #endif + #if TK2D + TK2dFont, TK2dCollection, + #endif + #if TextMeshPro + TextMeshPFont, + #endif + #if SVG + SVGAsset, + #endif + Object, + Video + } + + public enum TranslationFlag : byte + { + Normal = 1, + AutoTranslated = 2 + } + + + [Serializable] + public class TermData + { + public string Term = string.Empty; + public eTermType TermType = eTermType.Text; + + #if !UNITY_EDITOR + [NonSerialized] + #endif + public string Description; + + public string[] Languages = Array.Empty(); + public byte[] Flags = Array.Empty(); // flags for each translation + + [SerializeField] private string[] Languages_Touch; // TO BE REMOVED IN A FUTURE RELEASE + + public string GetTranslation ( int idx, string specialization=null, bool editMode=false ) + { + string text = Languages[idx]; + if (text != null) + { + text = SpecializationManager.GetSpecializedText(text, specialization); + if (!editMode) + { + text = text.Replace("[i2nt]", "").Replace("[/i2nt]", ""); + } + } + return text; + } + + public void SetTranslation( int idx, string translation, string specialization = null) + { + Languages[idx] = SpecializationManager.SetSpecializedText( Languages[idx], translation, specialization); + } + + public void RemoveSpecialization(string specialization) + { + for (int i = 0; i < Languages.Length; ++i) + RemoveSpecialization(i, specialization); + } + + + public void RemoveSpecialization( int idx, string specialization ) + { + var text = Languages[idx]; + if (specialization == "Any" || !text.Contains("[i2s_" + specialization + "]")) + { + return; + } + + var dict = SpecializationManager.GetSpecializations(text); + dict.Remove(specialization); + Languages[idx] = SpecializationManager.SetSpecializedText(dict); + } + + public bool IsAutoTranslated( int idx, bool IsTouch ) + { + return (Flags[idx] & (byte)TranslationFlag.AutoTranslated) > 0; + } + + public void Validate () + { + int nLanguages = Mathf.Max(Languages.Length, Flags.Length); + + if (Languages.Length != nLanguages) Array.Resize(ref Languages, nLanguages); + if (Flags.Length!=nLanguages) Array.Resize(ref Flags, nLanguages); + + if (Languages_Touch != null) + { + for (int i = 0; i < Mathf.Min(Languages_Touch.Length, nLanguages); ++i) + { + if (string.IsNullOrEmpty(Languages[i]) && !string.IsNullOrEmpty(Languages_Touch[i])) + { + Languages[i] = Languages_Touch[i]; + Languages_Touch[i] = null; + } + } + Languages_Touch = null; + } + } + + public bool IsTerm( string name, bool allowCategoryMistmatch) + { + if (!allowCategoryMistmatch) + return name == Term; + + return name == LanguageSourceData.GetKeyFromFullTerm (Term); + } + + public bool HasSpecializations() + { + for (int i = 0; i < Languages.Length; ++i) + { + if (!string.IsNullOrEmpty(Languages[i]) && Languages[i].Contains("[i2s_")) + return true; + } + return false; + } + + public List GetAllSpecializations() + { + List values = new List(); + for (int i = 0; i < Languages.Length; ++i) + SpecializationManager.AppendSpecializations(Languages[i], values); + return values; + } + } + + public class TermsPopup : PropertyAttribute + { + public TermsPopup(string filter = "") + { + Filter = filter; + } + + public string Filter { get; private set; } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/TermData.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/TermData.cs.meta new file mode 100644 index 00000000..5a63fee5 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/TermData.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: e04d3f1e4351c9740ad1815d63ede4cd +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils.meta new file mode 100644 index 00000000..be4c36ef --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 23035eaab7d1f024cb7e7b5bae9b078f +folderAsset: yes +timeCreated: 1478234053 +licenseType: Store +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/AutoChangeCultureInfo.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/AutoChangeCultureInfo.cs new file mode 100644 index 00000000..39a3f7c8 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/AutoChangeCultureInfo.cs @@ -0,0 +1,13 @@ +using UnityEngine; + +namespace TEngine.Localization +{ + + public class AutoChangeCultureInfo : MonoBehaviour + { + public void Start() + { + LocalizationManager.EnableChangingCultureInfo(true); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/AutoChangeCultureInfo.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/AutoChangeCultureInfo.cs.meta new file mode 100644 index 00000000..864ccef2 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/AutoChangeCultureInfo.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 9d7ad57256fd47940aba8035315bd2ca +timeCreated: 1478233236 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/CoroutineManager.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/CoroutineManager.cs new file mode 100644 index 00000000..9dc04e0d --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/CoroutineManager.cs @@ -0,0 +1,53 @@ +using System.Collections; +using UnityEditor; +using UnityEngine; + +namespace TEngine.Localization +{ + // This class is used to spawn coroutines from outside of MonoBehaviors + public class CoroutineManager : MonoBehaviour + { + static CoroutineManager pInstance + { + get{ + if (mInstance==null) + { + GameObject GO = new GameObject( "_Coroutiner" ); + GO.hideFlags = HideFlags.HideAndDontSave; + mInstance = GO.AddComponent(); + if (Application.isPlaying) + DontDestroyOnLoad(GO); + } + return mInstance; + } + } + static CoroutineManager mInstance; + + + private void Awake() + { + if (Application.isPlaying) + DontDestroyOnLoad(gameObject); + } + + public static Coroutine Start(IEnumerator coroutine) + { + #if UNITY_EDITOR + // Special case to allow coroutines to run in the Editor + if (!Application.isPlaying) + { + EditorApplication.CallbackFunction delg=null; + delg = delegate + { + if (!coroutine.MoveNext()) + EditorApplication.update -= delg; + }; + EditorApplication.update += delg; + return null; + } + #endif + + return pInstance.StartCoroutine(coroutine); + } + } +} diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/CoroutineManager.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/CoroutineManager.cs.meta new file mode 100644 index 00000000..28a08d59 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/CoroutineManager.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 20c75ea9a203e3d40aafe9b20abbd228 +timeCreated: 1502501187 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/CustomLocalizeCallback.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/CustomLocalizeCallback.cs new file mode 100644 index 00000000..85083b39 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/CustomLocalizeCallback.cs @@ -0,0 +1,27 @@ +using UnityEngine; +using UnityEngine.Events; + +namespace TEngine.Localization +{ + [AddComponentMenu("I2/Localization/I2 Localize Callback")] + public class CustomLocalizeCallback : MonoBehaviour + { + public UnityEvent _OnLocalize = new UnityEvent(); + + public void OnEnable() + { + LocalizationManager.OnLocalizeEvent -= OnLocalize; + LocalizationManager.OnLocalizeEvent += OnLocalize; + } + + public void OnDisable() + { + LocalizationManager.OnLocalizeEvent -= OnLocalize; + } + + public void OnLocalize() + { + _OnLocalize.Invoke(); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/CustomLocalizeCallback.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/CustomLocalizeCallback.cs.meta new file mode 100644 index 00000000..5202237e --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/CustomLocalizeCallback.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 24da539b4435ced4da00f418327a772c +timeCreated: 1520536965 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/HindiFixer.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/HindiFixer.cs new file mode 100644 index 00000000..a12b46cb --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/HindiFixer.cs @@ -0,0 +1,157 @@ +using System.Linq; + +namespace TEngine.Localization +{ + + public class HindiFixer + { + + // Needs to also implement: Hindi: https://www.microsoft.com/typography/OpenTypeDev/devanagari/intro.htm + //https://social.msdn.microsoft.com/Forums/windows/en-US/9883ff08-bd72-499b-9543-ed424167281d/converting-hindi-text-to-english-text?forum=winforms + internal static string Fix(string text) + { + while (true) + { + char[] arr = text.ToCharArray(); + bool changed = false; + + for (int i = 0; i < arr.Length; ++i) + { + // interchange the order of "i" vowel + if (arr[i] == 2367 && !char.IsWhiteSpace(arr[i - 1]) && arr[i - 1]!=0) + { + arr[i] = arr[i - 1]; + arr[i - 1] = (char)2367; + changed = true; + } + + if (i == arr.Length - 1) + continue; + + // letter "I" + Nukta forms letter vocalic "L" + if (arr[i] == 2311) + { + if (arr[i + 1] == 2364) + { + arr[i] = (char)2316; + arr[i + 1] = (char)0; + changed = true; + } + } + + // vowel sign vocalic "R" + sign Nukta forms vowel sign vocalic "Rr" + if (arr[i] == 2371) + { + if (arr[i + 1] == 2364) + { + arr[i] = (char)2372; + arr[i + 1] = (char)0; + changed = true; + } + } + + // Candrabindu + sign Nukta forms Om + if (arr[i] == 2305) + { + if (arr[i + 1] == 2364) + { + arr[i] = (char)2384; + arr[i + 1] = (char)0; + changed = true; + } + } + + // letter vocalic "R" + sign Nukta forms letter vocalic "Rr" + if (arr[i] == 2315) + { + if (arr[i + 1] == 2364) + { + arr[i] = (char)2400; + arr[i + 1] = (char)0; + changed = true; + } + } + + // letter "Ii" + sign Nukta forms letter vocalic "LI" + if (arr[i] == 2312) + { + if (arr[i + 1] == 2364) + { + arr[i] = (char)2401; + arr[i + 1] = (char)0; + changed = true; + } + } + + // vowel sign "I" + sign Nukta forms vowel sign vocalic "L" + if (arr[i] == 2367) + { + if (arr[i + 1] == 2364) + { + arr[i] = (char)2402; + arr[i + 1] = (char)0; + changed = true; + } + } + + // vowel sign "Ii" + sign Nukta forms vowel sign vocalic "LI" + if (arr[i] == 2368) + { + if (arr[i + 1] == 2364) + { + arr[i] = (char)2403; + arr[i + 1] = (char)0; + changed = true; + } + } + + // Danda + sign Nukta forms sign Avagraha + if (arr[i] == 2404) + { + if (arr[i + 1] == 2364) + { + arr[i] = (char)2365; + arr[i + 1] = (char)0; + changed = true; + } + } + + // consonant + Halant + Halant + consonant forms consonant + Halant + ZWNJ + consonant + //if (arr[i] == 2381) + //{ + // if (arr[i + 1] == 2381) + // { + // arr[i+1] = (char)8204; // + // } + //} + + // consonant + Halant + Nukta + consonant forms consonant + Halant + ZWJ + Consonant + //if (arr[i] == 2364) + //{ + // if (arr[i + 1] == 2381) + // { + // arr[i] = (char)2381; // + // arr[i+1] = (char)8205; // + // } + //} + /*if (arr[i] == 0x938 && arr[i + 1] == 0x94D)//थ') + { + arr[i] = (char)0x930; + arr[i + 1] = (char)0; + }*/ + } + + if (!changed) + { + return text; + } + + var newText = new string(arr.Where(x => x != 0).ToArray()); + if (newText == text) + return newText; + text = newText; + return text; // remove this later to allow for several passes + } + } + } +} diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/HindiFixer.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/HindiFixer.cs.meta new file mode 100644 index 00000000..1db8cff8 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/HindiFixer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 7b58f53b44162054f8b586f0cdbe43ba +timeCreated: 1506968129 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/I2Utils.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/I2Utils.cs new file mode 100644 index 00000000..adb1e84b --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/I2Utils.cs @@ -0,0 +1,335 @@ +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using UnityEditor; +using UnityEngine; +using UnityEngine.Networking; +using UnityEngine.SceneManagement; + +namespace TEngine.Localization +{ + public static class I2Utils + { + public const string ValidChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; + public const string NumberChars = "0123456789"; + public const string ValidNameSymbols = ".-_$#@*()[]{}+:?!&',^=<>~`"; + + public static string ReverseText(string source) + { + int len = source.Length; + char[] output = new char[len]; + + char[] separators = { '\r', '\n' }; + for (int istart = 0; istart' && tagType == '<'))) + { + // We've found the end of the tag, so stop skipping characters + insideTag = false; + shouldAppend = false; // Don't append the closing tag character + } + else if (insideTag) + { + // We're inside a tag, so don't append the current character + shouldAppend = false; + } + + // Append the current character if it's not part of a tag + if (shouldAppend) + { + char new_char = ' '; + if (allowCategory && (c == '\\' || c == '\"' || c == '/') || + char.IsLetterOrDigit(c) || + ValidNameSymbols.IndexOf(c) >= 0) + { + new_char = c; + } + + if (char.IsWhiteSpace(c)) + { + if (!skipped) + { + if (result.Length > 0) + result.Append(' '); + + skipped = true; + } + } + else + { + skipped = false; + result.Append(new_char); + } + } + } + + return result.ToString(); // Convert the StringBuilder to a string and return it + } + + public static string SplitLine(string line, int maxCharacters) + { + if (maxCharacters <= 0 || line.Length < maxCharacters) + return line; + + var chars = line.ToCharArray(); + bool insideOfLine = true; + bool allowNewLine = false; + for (int i = 0, nCharsInLine = 0; i < chars.Length; ++i) + { + if (insideOfLine) + { + nCharsInLine++; + if (chars[i] == '\n') + { + nCharsInLine = 0; + } + if (nCharsInLine >= maxCharacters && char.IsWhiteSpace(chars[i])) + { + chars[i] = '\n'; + insideOfLine = false; + allowNewLine = false; + } + } + else + { + if (!char.IsWhiteSpace(chars[i])) + { + insideOfLine = true; + nCharsInLine = 0; + } + else + { + if (chars[i] != '\n') + { + chars[i] = (char)0; + } + else + { + if (!allowNewLine) + chars[i] = (char)0; + allowNewLine = true; + } + } + } + } + + return new string(chars.Where(c => c != (char)0).ToArray()); + } + + public static bool FindNextTag(string line, int iStart, out int tagStart, out int tagEnd) + { + tagStart = -1; + tagEnd = -1; + int len = line.Length; + + // Find where the tag starts + for (tagStart = iStart; tagStart < len; ++tagStart) + if (line[tagStart] == '[' || line[tagStart] == '(' || line[tagStart] == '{' || line[tagStart] == '<') + break; + + if (tagStart == len) + return false; + + bool isArabic = false; + for (tagEnd = tagStart + 1; tagEnd < len; ++tagEnd) + { + char c = line[tagEnd]; + if (c == ']' || c == ')' || c == '}' || c=='>') + { + if (isArabic) return FindNextTag(line, tagEnd + 1, out tagStart, out tagEnd); + return true; + } + if (c > 255) isArabic = true; + } + + // there is an open, but not close character + return false; + } + + public static string RemoveTags(string text) + { + return Regex.Replace(text, @"\{\[(.*?)]}|\[(.*?)]|\<(.*?)>", ""); + } + + public static bool RemoveResourcesPath(ref string sPath) + { + int Ind1 = sPath.IndexOf("\\Resources\\", StringComparison.Ordinal); + int Ind2 = sPath.IndexOf("\\Resources/", StringComparison.Ordinal); + int Ind3 = sPath.IndexOf("/Resources\\", StringComparison.Ordinal); + int Ind4 = sPath.IndexOf("/Resources/", StringComparison.Ordinal); + int Index = Mathf.Max(Ind1, Ind2, Ind3, Ind4); + bool IsResource = false; + if (Index >= 0) + { + sPath = sPath.Substring(Index + 11); + IsResource = true; + } + else + { + // If its not in the Resources, then it has to be in the References + // Therefore, the path has to be stripped and let only the name + Index = sPath.LastIndexOfAny(LanguageSourceData.CategorySeparators); + if (Index > 0) + sPath = sPath.Substring(Index + 1); + } + + string Extension = Path.GetExtension(sPath); + if (!string.IsNullOrEmpty(Extension)) + sPath = sPath.Substring(0, sPath.Length - Extension.Length); + + return IsResource; + } + + public static bool IsPlaying() + { + if (Application.isPlaying) + return true; + #if UNITY_EDITOR + return EditorApplication.isPlayingOrWillChangePlaymode; + #else + return false; + #endif + } + + public static string GetPath(this Transform tr) + { + var parent = tr.parent; + if (tr == null) + return tr.name; + return parent.GetPath() + "/" + tr.name; + } + +#if UNITY_5_3_OR_NEWER + public static Transform FindObject(string objectPath) + { + return FindObject(SceneManager.GetActiveScene(), objectPath); + } + + + public static Transform FindObject(UnityEngine.SceneManagement.Scene scene, string objectPath) + { + //var roots = SceneManager.GetActiveScene().GetRootGameObjects(); + var roots = scene.GetRootGameObjects(); + for (int i=0; i(Transform tr) where H : Component + { + if (!tr) + return null; + + H comp = tr.GetComponent(); + while (!comp && tr) + { + comp = tr.GetComponent(); + tr = tr.parent; + } + return comp; + } + + public static string GetCaptureMatch(Match match) + { + for (int i = match.Groups.Count - 1; i >= 0; --i) + if (match.Groups[i].Success) + { + return match.Groups[i].ToString(); + } + return match.ToString(); + } + + public static void SendWebRequest(UnityWebRequest www ) + { + #if UNITY_2017_2_OR_NEWER + www.SendWebRequest(); + #else + www.Send(); + #endif + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/I2Utils.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/I2Utils.cs.meta new file mode 100644 index 00000000..bae94eba --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/I2Utils.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 7a974f5f5b8a8c94abaf68d987ea0662 +timeCreated: 1516389217 +licenseType: Store +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/LocalizationParamsManager.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/LocalizationParamsManager.cs new file mode 100644 index 00000000..14936fc9 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/LocalizationParamsManager.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +namespace TEngine.Localization +{ + public interface ILocalizationParamsManager + { + string GetParameterValue( string Param ); + } + + public class LocalizationParamsManager : MonoBehaviour, ILocalizationParamsManager + { + [Serializable] + public struct ParamValue + { + public string Name, Value; + + } + + [SerializeField] + public List _Params = new List(); + + public bool _IsGlobalManager; + + public string GetParameterValue( string ParamName ) + { + if (_Params != null) + { + for (int i = 0, imax = _Params.Count; i < imax; ++i) + if (_Params[i].Name == ParamName) + return _Params[i].Value; + } + return null; // not found + } + + public void SetParameterValue( string ParamName, string ParamValue, bool localize = true ) + { + bool setted = false; + for (int i = 0, imax = _Params.Count; i < imax; ++i) + if (_Params[i].Name == ParamName) + { + var temp = _Params[i]; + temp.Value = ParamValue; + _Params[i] = temp; + setted = true; + break; + } + if (!setted) + _Params.Add(new ParamValue { Name = ParamName, Value = ParamValue }); + + if (localize) + OnLocalize(); + } + + public void OnLocalize() + { + var loc = GetComponent(); + if (loc != null) + loc.OnLocalize(true); + } + + public virtual void OnEnable() + { + if (_IsGlobalManager) + DoAutoRegister(); + } + + //[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + //public void AutoStart() + //{ + // if (_AutoRegister) + // DoAutoRegister(); + //} + + public void DoAutoRegister() + { + if (!LocalizationManager.ParamManagers.Contains(this)) + { + LocalizationManager.ParamManagers.Add(this); + LocalizationManager.LocalizeAll(true); + } + } + + public void OnDisable() + { + LocalizationManager.ParamManagers.Remove(this); + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/LocalizationParamsManager.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/LocalizationParamsManager.cs.meta new file mode 100644 index 00000000..0e6360e6 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/LocalizationParamsManager.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: c9586cfd190ca384a8dd72aa92c86152 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/LocalizedString.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/LocalizedString.cs new file mode 100644 index 00000000..f5428575 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/LocalizedString.cs @@ -0,0 +1,42 @@ +using System; + +namespace TEngine.Localization +{ + [Serializable] + public struct LocalizedString + { + public string mTerm; + public bool mRTL_IgnoreArabicFix; + public int mRTL_MaxLineLength; + public bool mRTL_ConvertNumbers; + public bool m_DontLocalizeParameters; + + public static implicit operator string(LocalizedString s) + { + return s.ToString(); + } + + public static implicit operator LocalizedString(string term) + { + return new LocalizedString { mTerm = term }; + } + + public LocalizedString (LocalizedString str) + { + mTerm = str.mTerm; + mRTL_IgnoreArabicFix = str.mRTL_IgnoreArabicFix; + mRTL_MaxLineLength = str.mRTL_MaxLineLength; + mRTL_ConvertNumbers = str.mRTL_ConvertNumbers; + m_DontLocalizeParameters = str.m_DontLocalizeParameters; + } + + + + public override string ToString() + { + var translation = LocalizationManager.GetTranslation(mTerm, !mRTL_IgnoreArabicFix, mRTL_MaxLineLength, !mRTL_ConvertNumbers, true ); + LocalizationManager.ApplyLocalizationParams(ref translation, !m_DontLocalizeParameters); + return translation; + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/LocalizedString.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/LocalizedString.cs.meta new file mode 100644 index 00000000..e3a9cd2f --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/LocalizedString.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 0bf401b4b1a2c364ba3795d47b95835f +timeCreated: 1478236841 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RTLFixer.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RTLFixer.cs new file mode 100644 index 00000000..5c256d63 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RTLFixer.cs @@ -0,0 +1,971 @@ +using System; +using System.Collections.Generic; + +namespace TEngine.Localization +{ + + public class RTLFixer + { + /// + /// Fix the specified string. + /// + /// + /// String to be fixed. + /// + public static string Fix(string str) + { + return Fix(str, false, true); + } + + public static string Fix(string str, bool rtl) + { + if (rtl) + + { + return Fix(str); + } + + string[] words = str.Split(' '); + string result = ""; + string arabicToIgnore = ""; + foreach (string word in words) + { + if (char.IsLower(word.ToLower()[word.Length / 2])) + { + result += Fix(arabicToIgnore) + word + " "; + arabicToIgnore = ""; + } + else + { + arabicToIgnore += word + " "; + + } + } + if (arabicToIgnore != "") + result += Fix(arabicToIgnore); + + return result; + } + + /// + /// Fix the specified string with customization options. + /// + /// + /// String to be fixed. + /// + /// + /// Show tashkeel. + /// + /// + /// Use hindu numbers. + /// + public static string Fix(string str, bool showTashkeel, bool useHinduNumbers) + { + var newStr = HindiFixer.Fix(str); + if (newStr != str) + return newStr; + RTLFixerTool.showTashkeel = showTashkeel; + RTLFixerTool.useHinduNumbers = useHinduNumbers; + + if (str.Contains("\n")) + str = str.Replace("\n", Environment.NewLine); + + if (str.Contains(Environment.NewLine)) + { + string[] stringSeparators = { Environment.NewLine }; + string[] strSplit = str.Split(stringSeparators, StringSplitOptions.None); + + if (strSplit.Length == 0) + return RTLFixerTool.FixLine(str); + if (strSplit.Length == 1) + return RTLFixerTool.FixLine(str); + string outputString = RTLFixerTool.FixLine(strSplit[0]); + int iteration = 1; + if (strSplit.Length > 1) + { + while (iteration < strSplit.Length) + { + outputString += Environment.NewLine + RTLFixerTool.FixLine(strSplit[iteration]); + iteration++; + } + } + return outputString; + } + + return RTLFixerTool.FixLine(str); + + } + + } + + + /// + /// Arabic Contextual forms General - Unicode + /// + internal enum IsolatedArabicLetters + { + Hamza = 0xFE80, + Alef = 0xFE8D, + AlefHamza = 0xFE83, + WawHamza = 0xFE85, + AlefMaksoor = 0xFE87, + AlefMaksora = 0xFBFC, + HamzaNabera = 0xFE89, + Ba = 0xFE8F, + Ta = 0xFE95, + Tha2 = 0xFE99, + Jeem = 0xFE9D, + H7aa = 0xFEA1, + Khaa2 = 0xFEA5, + Dal = 0xFEA9, + Thal = 0xFEAB, + Ra2 = 0xFEAD, + Zeen = 0xFEAF, + Seen = 0xFEB1, + Sheen = 0xFEB5, + S9a = 0xFEB9, + Dha = 0xFEBD, + T6a = 0xFEC1, + T6ha = 0xFEC5, + Ain = 0xFEC9, + Gain = 0xFECD, + Fa = 0xFED1, + Gaf = 0xFED5, + Kaf = 0xFED9, + Lam = 0xFEDD, + Meem = 0xFEE1, + Noon = 0xFEE5, + Ha = 0xFEE9, + Waw = 0xFEED, + Ya = 0xFEF1, + AlefMad = 0xFE81, + TaMarboota = 0xFE93, + PersianPe = 0xFB56, // Persian Letters; + PersianChe = 0xFB7A, + PersianZe = 0xFB8A, + PersianGaf = 0xFB92, + PersianGaf2 = 0xFB8E + + } + + /// + /// Arabic Contextual forms - Isolated + /// + internal enum GeneralArabicLetters + { + Hamza = 0x0621, + Alef = 0x0627, + AlefHamza = 0x0623, + WawHamza = 0x0624, + AlefMaksoor = 0x0625, + AlefMagsora = 0x0649, + HamzaNabera = 0x0626, + Ba = 0x0628, + Ta = 0x062A, + Tha2 = 0x062B, + Jeem = 0x062C, + H7aa = 0x062D, + Khaa2 = 0x062E, + Dal = 0x062F, + Thal = 0x0630, + Ra2 = 0x0631, + Zeen = 0x0632, + Seen = 0x0633, + Sheen = 0x0634, + S9a = 0x0635, + Dha = 0x0636, + T6a = 0x0637, + T6ha = 0x0638, + Ain = 0x0639, + Gain = 0x063A, + Fa = 0x0641, + Gaf = 0x0642, + Kaf = 0x0643, + Lam = 0x0644, + Meem = 0x0645, + Noon = 0x0646, + Ha = 0x0647, + Waw = 0x0648, + Ya = 0x064A, + AlefMad = 0x0622, + TaMarboota = 0x0629, + PersianPe = 0x067E, // Persian Letters; + PersianChe = 0x0686, + PersianZe = 0x0698, + PersianGaf = 0x06AF, + PersianGaf2 = 0x06A9 + + } + + /// + /// Data Structure for conversion + /// + internal class ArabicMapping + { + public int from; + public int to; + public ArabicMapping(int from, int to) + { + this.from = from; + this.to = to; + } + } + + /// + /// Sets up and creates the conversion table + /// + internal class ArabicTable + { + + private static List mapList; + private static ArabicTable arabicMapper; + + /// + /// Setting up the conversion table + /// + private ArabicTable() + { + mapList = new List(); + + + + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Hamza, (int)IsolatedArabicLetters.Hamza)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Alef, (int)IsolatedArabicLetters.Alef)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.AlefHamza, (int)IsolatedArabicLetters.AlefHamza)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.WawHamza, (int)IsolatedArabicLetters.WawHamza)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.AlefMaksoor, (int)IsolatedArabicLetters.AlefMaksoor)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.AlefMagsora, (int)IsolatedArabicLetters.AlefMaksora)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.HamzaNabera, (int)IsolatedArabicLetters.HamzaNabera)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Ba, (int)IsolatedArabicLetters.Ba)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Ta, (int)IsolatedArabicLetters.Ta)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Tha2, (int)IsolatedArabicLetters.Tha2)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Jeem, (int)IsolatedArabicLetters.Jeem)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.H7aa, (int)IsolatedArabicLetters.H7aa)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Khaa2, (int)IsolatedArabicLetters.Khaa2)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Dal, (int)IsolatedArabicLetters.Dal)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Thal, (int)IsolatedArabicLetters.Thal)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Ra2, (int)IsolatedArabicLetters.Ra2)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Zeen, (int)IsolatedArabicLetters.Zeen)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Seen, (int)IsolatedArabicLetters.Seen)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Sheen, (int)IsolatedArabicLetters.Sheen)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.S9a, (int)IsolatedArabicLetters.S9a)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Dha, (int)IsolatedArabicLetters.Dha)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.T6a, (int)IsolatedArabicLetters.T6a)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.T6ha, (int)IsolatedArabicLetters.T6ha)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Ain, (int)IsolatedArabicLetters.Ain)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Gain, (int)IsolatedArabicLetters.Gain)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Fa, (int)IsolatedArabicLetters.Fa)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Gaf, (int)IsolatedArabicLetters.Gaf)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Kaf, (int)IsolatedArabicLetters.Kaf)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Lam, (int)IsolatedArabicLetters.Lam)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Meem, (int)IsolatedArabicLetters.Meem)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Noon, (int)IsolatedArabicLetters.Noon)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Ha, (int)IsolatedArabicLetters.Ha)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Waw, (int)IsolatedArabicLetters.Waw)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.Ya, (int)IsolatedArabicLetters.Ya)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.AlefMad, (int)IsolatedArabicLetters.AlefMad)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.TaMarboota, (int)IsolatedArabicLetters.TaMarboota)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.PersianPe, (int)IsolatedArabicLetters.PersianPe)); // Persian Letters; + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.PersianChe, (int)IsolatedArabicLetters.PersianChe)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.PersianZe, (int)IsolatedArabicLetters.PersianZe)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.PersianGaf, (int)IsolatedArabicLetters.PersianGaf)); + mapList.Add(new ArabicMapping((int)GeneralArabicLetters.PersianGaf2, (int)IsolatedArabicLetters.PersianGaf2)); + + + + + //for (int i = 0; i < generalArabic.Length; i++) + // mapList.Add(new ArabicMapping((int)generalArabic.GetValue(i), (int)isolatedArabic.GetValue(i))); // I + + + } + + /// + /// Singleton design pattern, Get the mapper. If it was not created before, create it. + /// + internal static ArabicTable ArabicMapper + { + get + { + if (arabicMapper == null) + arabicMapper = new ArabicTable(); + return arabicMapper; + } + } + + internal int Convert(int toBeConverted) + { + + foreach (ArabicMapping arabicMap in mapList) + if (arabicMap.from == toBeConverted) + { + return arabicMap.to; + } + return toBeConverted; + } + + + } + + + internal class TashkeelLocation + { + public char tashkeel; + public int position; + public TashkeelLocation(char tashkeel, int position) + { + this.tashkeel = tashkeel; + this.position = position; + } + } + + + internal class RTLFixerTool + { + internal static bool showTashkeel = true; + internal static bool useHinduNumbers; + + + internal static string RemoveTashkeel(string str, out List tashkeelLocation) + { + tashkeelLocation = new List(); + char[] letters = str.ToCharArray(); + + int index = 0; + for (int i = 0; i < letters.Length; i++) + { + if (letters[i] == (char)0x064B) + { // Tanween Fatha + tashkeelLocation.Add(new TashkeelLocation((char)0x064B, i)); + index++; + } + else if (letters[i] == (char)0x064C) + { // DAMMATAN + tashkeelLocation.Add(new TashkeelLocation((char)0x064C, i)); + index++; + } + else if (letters[i] == (char)0x064D) + { // KASRATAN + tashkeelLocation.Add(new TashkeelLocation((char)0x064D, i)); + index++; + } + else if (letters[i] == (char)0x064E) + { // FATHA + if (index > 0) + { + if (tashkeelLocation[index - 1].tashkeel == (char)0x0651) // SHADDA + { + tashkeelLocation[index - 1].tashkeel = (char)0xFC60; // Shadda With Fatha + continue; + } + } + + tashkeelLocation.Add(new TashkeelLocation((char)0x064E, i)); + index++; + } + else if (letters[i] == (char)0x064F) + { // DAMMA + if (index > 0) + { + if (tashkeelLocation[index - 1].tashkeel == (char)0x0651) + { // SHADDA + tashkeelLocation[index - 1].tashkeel = (char)0xFC61; // Shadda With DAMMA + continue; + } + } + tashkeelLocation.Add(new TashkeelLocation((char)0x064F, i)); + index++; + } + else if (letters[i] == (char)0x0650) + { // KASRA + if (index > 0) + { + if (tashkeelLocation[index - 1].tashkeel == (char)0x0651) + { // SHADDA + tashkeelLocation[index - 1].tashkeel = (char)0xFC62; // Shadda With KASRA + continue; + } + } + tashkeelLocation.Add(new TashkeelLocation((char)0x0650, i)); + index++; + } + else if (letters[i] == (char)0x0651) + { // SHADDA + if (index > 0) + { + if (tashkeelLocation[index - 1].tashkeel == (char)0x064E) // FATHA + { + tashkeelLocation[index - 1].tashkeel = (char)0xFC60; // Shadda With Fatha + continue; + } + + if (tashkeelLocation[index - 1].tashkeel == (char)0x064F) // DAMMA + { + tashkeelLocation[index - 1].tashkeel = (char)0xFC61; // Shadda With DAMMA + continue; + } + + if (tashkeelLocation[index - 1].tashkeel == (char)0x0650) // KASRA + { + tashkeelLocation[index - 1].tashkeel = (char)0xFC62; // Shadda With KASRA + continue; + } + } + + tashkeelLocation.Add(new TashkeelLocation((char)0x0651, i)); + index++; + } + else if (letters[i] == (char)0x0652) + { // SUKUN + tashkeelLocation.Add(new TashkeelLocation((char)0x0652, i)); + index++; + } + else if (letters[i] == (char)0x0653) + { // MADDAH ABOVE + tashkeelLocation.Add(new TashkeelLocation((char)0x0653, i)); + index++; + } + } + + string[] split = str.Split((char)0x064B, (char)0x064C, (char)0x064D, (char)0x064E, (char)0x064F, (char)0x0650, (char)0x0651, (char)0x0652, (char)0x0653, (char)0xFC60, (char)0xFC61, (char)0xFC62); + str = ""; + + foreach (string s in split) + { + str += s; + } + + return str; + } + + internal static char[] ReturnTashkeel(char[] letters, List tashkeelLocation) + { + char[] lettersWithTashkeel = new char[letters.Length + tashkeelLocation.Count]; + + int letterWithTashkeelTracker = 0; + for (int i = 0; i < letters.Length; i++) + { + lettersWithTashkeel[letterWithTashkeelTracker] = letters[i]; + letterWithTashkeelTracker++; + foreach (TashkeelLocation hLocation in tashkeelLocation) + { + if (hLocation.position == letterWithTashkeelTracker) + { + lettersWithTashkeel[letterWithTashkeelTracker] = hLocation.tashkeel; + letterWithTashkeelTracker++; + } + } + } + + return lettersWithTashkeel; + } + + /// + /// Converts a string to a form in which the sting will be displayed correctly for arabic text. + /// + /// String to be converted. Example: "Aaa" + /// Converted string. Example: "aa aaa A" without the spaces. + internal static string FixLine(string str) + { + string test = ""; + + List tashkeelLocation; + + string originString = RemoveTashkeel(str, out tashkeelLocation); + + char[] lettersOrigin = originString.ToCharArray(); + char[] lettersFinal = originString.ToCharArray(); + + + + for (int i = 0; i < lettersOrigin.Length; i++) + { + lettersOrigin[i] = (char)ArabicTable.ArabicMapper.Convert(lettersOrigin[i]); + } + + for (int i = 0; i < lettersOrigin.Length; i++) + { + bool skip = false; + + + //lettersOrigin[i] = (char)ArabicTable.ArabicMapper.Convert(lettersOrigin[i]); + + + // For special Lam Letter connections. + if (lettersOrigin[i] == (char)IsolatedArabicLetters.Lam) + { + + if (i < lettersOrigin.Length - 1) + { + //lettersOrigin[i + 1] = (char)ArabicTable.ArabicMapper.Convert(lettersOrigin[i + 1]); + if (lettersOrigin[i + 1] == (char)IsolatedArabicLetters.AlefMaksoor) + { + lettersOrigin[i] = (char)0xFEF7; + lettersFinal[i + 1] = (char)0xFFFF; + skip = true; + } + else if (lettersOrigin[i + 1] == (char)IsolatedArabicLetters.Alef) + { + lettersOrigin[i] = (char)0xFEF9; + lettersFinal[i + 1] = (char)0xFFFF; + skip = true; + } + else if (lettersOrigin[i + 1] == (char)IsolatedArabicLetters.AlefHamza) + { + lettersOrigin[i] = (char)0xFEF5; + lettersFinal[i + 1] = (char)0xFFFF; + skip = true; + } + else if (lettersOrigin[i + 1] == (char)IsolatedArabicLetters.AlefMad) + { + lettersOrigin[i] = (char)0xFEF3; + lettersFinal[i + 1] = (char)0xFFFF; + skip = true; + } + } + + } + + + if (!IsIgnoredCharacter(lettersOrigin[i])) + { + if (IsMiddleLetter(lettersOrigin, i)) + lettersFinal[i] = (char)(lettersOrigin[i] + 3); + else if (IsFinishingLetter(lettersOrigin, i)) + lettersFinal[i] = (char)(lettersOrigin[i] + 1); + else if (IsLeadingLetter(lettersOrigin, i)) + lettersFinal[i] = (char)(lettersOrigin[i] + 2); + } + + //string strOut = String.Format(@"\x{0:x4}", (ushort)lettersOrigin[i]); + //UnityEngine.Debug.Log(strOut); + + //strOut = String.Format(@"\x{0:x4}", (ushort)lettersFinal[i]); + //UnityEngine.Debug.Log(strOut); + + test += Convert.ToString(lettersOrigin[i], 16) + " "; + if (skip) + i++; + + + //chaning numbers to hindu + if (useHinduNumbers) + { + if (lettersOrigin[i] == (char)0x0030) + lettersFinal[i] = (char)0x0660; + else if (lettersOrigin[i] == (char)0x0031) + lettersFinal[i] = (char)0x0661; + else if (lettersOrigin[i] == (char)0x0032) + lettersFinal[i] = (char)0x0662; + else if (lettersOrigin[i] == (char)0x0033) + lettersFinal[i] = (char)0x0663; + else if (lettersOrigin[i] == (char)0x0034) + lettersFinal[i] = (char)0x0664; + else if (lettersOrigin[i] == (char)0x0035) + lettersFinal[i] = (char)0x0665; + else if (lettersOrigin[i] == (char)0x0036) + lettersFinal[i] = (char)0x0666; + else if (lettersOrigin[i] == (char)0x0037) + lettersFinal[i] = (char)0x0667; + else if (lettersOrigin[i] == (char)0x0038) + lettersFinal[i] = (char)0x0668; + else if (lettersOrigin[i] == (char)0x0039) + lettersFinal[i] = (char)0x0669; + } + + } + + + + //Return the Tashkeel to their places. + if (showTashkeel) + lettersFinal = ReturnTashkeel(lettersFinal, tashkeelLocation); + + + List list = new List(); + + List numberList = new List(); + + for (int i = lettersFinal.Length - 1; i >= 0; i--) + { + + + // if (lettersFinal[i] == '(') + // numberList.Add(')'); + // else if (lettersFinal[i] == ')') + // numberList.Add('('); + // else if (lettersFinal[i] == '<') + // numberList.Add('>'); + // else if (lettersFinal[i] == '>') + // numberList.Add('<'); + // else + if (char.IsPunctuation(lettersFinal[i]) && i > 0 && i < lettersFinal.Length - 1 && + (char.IsPunctuation(lettersFinal[i - 1]) || char.IsPunctuation(lettersFinal[i + 1]))) + { + if (lettersFinal[i] == '(') + list.Add(')'); + else if (lettersFinal[i] == ')') + list.Add('('); + else if (lettersFinal[i] == '<') + list.Add('>'); + else if (lettersFinal[i] == '>') + list.Add('<'); + else if (lettersFinal[i] == '[') + list.Add(']'); + else if (lettersFinal[i] == ']') + list.Add('['); + else if (lettersFinal[i] != 0xFFFF) + list.Add(lettersFinal[i]); + } + // For cases where english words and arabic are mixed. This allows for using arabic, english and numbers in one sentence. + else if (lettersFinal[i] == ' ' && i > 0 && i < lettersFinal.Length - 1 && + (char.IsLower(lettersFinal[i - 1]) || char.IsUpper(lettersFinal[i - 1]) || char.IsNumber(lettersFinal[i - 1])) && + (char.IsLower(lettersFinal[i + 1]) || char.IsUpper(lettersFinal[i + 1]) || char.IsNumber(lettersFinal[i + 1]))) + + { + numberList.Add(lettersFinal[i]); + } + + else if (char.IsNumber(lettersFinal[i]) || char.IsLower(lettersFinal[i]) || + char.IsUpper(lettersFinal[i]) || char.IsSymbol(lettersFinal[i]) || + char.IsPunctuation(lettersFinal[i]))// || lettersFinal[i] == '^') //) + { + + if (lettersFinal[i] == '(') + numberList.Add(')'); + else if (lettersFinal[i] == ')') + numberList.Add('('); + else if (lettersFinal[i] == '<') + numberList.Add('>'); + else if (lettersFinal[i] == '>') + numberList.Add('<'); + else if (lettersFinal[i] == '[') + list.Add(']'); + else if (lettersFinal[i] == ']') + list.Add('['); + else + numberList.Add(lettersFinal[i]); + } + else if (lettersFinal[i] >= (char)0xD800 && lettersFinal[i] <= (char)0xDBFF || + lettersFinal[i] >= (char)0xDC00 && lettersFinal[i] <= (char)0xDFFF) + { + numberList.Add(lettersFinal[i]); + } + else + { + if (numberList.Count > 0) + { + for (int j = 0; j < numberList.Count; j++) + list.Add(numberList[numberList.Count - 1 - j]); + numberList.Clear(); + } + if (lettersFinal[i] != 0xFFFF) + list.Add(lettersFinal[i]); + + } + } + if (numberList.Count > 0) + { + for (int j = 0; j < numberList.Count; j++) + list.Add(numberList[numberList.Count - 1 - j]); + numberList.Clear(); + } + + // Moving letters from a list to an array. + lettersFinal = new char[list.Count]; + for (int i = 0; i < lettersFinal.Length; i++) + lettersFinal[i] = list[i]; + + + str = new string(lettersFinal); + return str; + } + + /// + /// English letters, numbers and punctuation characters are ignored. This checks if the ch is an ignored character. + /// + /// The character to be checked for skipping + /// True if the character should be ignored, false if it should not be ignored. + internal static bool IsIgnoredCharacter(char ch) + { + bool isPunctuation = char.IsPunctuation(ch); + bool isNumber = char.IsNumber(ch); + bool isLower = char.IsLower(ch); + bool isUpper = char.IsUpper(ch); + bool isSymbol = char.IsSymbol(ch); + bool isPersianCharacter = ch == (char)0xFB56 || ch == (char)0xFB7A || ch == (char)0xFB8A || ch == (char)0xFB92 || ch == (char)0xFB8E; + bool isPresentationFormB = ch <= (char)0xFEFF && ch >= (char)0xFE70; + bool isAcceptableCharacter = isPresentationFormB || isPersianCharacter || ch == (char)0xFBFC; + + + + return isPunctuation || + isNumber || + isLower || + isUpper || + isSymbol || + !isAcceptableCharacter || + ch == 'a' || ch == '>' || ch == '<' || ch == (char)0x061B; + + // return char.IsPunctuation(ch) || char.IsNumber(ch) || ch == 'a' || ch == '>' || ch == '<' || + // char.IsLower(ch) || char.IsUpper(ch) || ch == (char)0x061B || char.IsSymbol(ch) + // || !(ch <= (char)0xFEFF && ch >= (char)0xFE70) // Presentation Form B + // || ch == (char)0xFB56 || ch == (char)0xFB7A || ch == (char)0xFB8A || ch == (char)0xFB92; // Persian Characters + + // PersianPe = 0xFB56, + // PersianChe = 0xFB7A, + // PersianZe = 0xFB8A, + // PersianGaf = 0xFB92 + //lettersOrigin[i] <= (char)0xFEFF && lettersOrigin[i] >= (char)0xFE70 + } + + /// + /// Checks if the letter at index value is a leading character in Arabic or not. + /// + /// The whole word that contains the character to be checked + /// The index of the character to be checked + /// True if the character at index is a leading character, else, returns false + internal static bool IsLeadingLetter(char[] letters, int index) + { + + bool lettersThatCannotBeBeforeALeadingLetter = index == 0 + || letters[index - 1] == ' ' + || letters[index - 1] == '*' // ??? Remove? + || letters[index - 1] == 'A' // ??? Remove? + || char.IsPunctuation(letters[index - 1]) + || letters[index - 1] == '>' + || letters[index - 1] == '<' + || letters[index - 1] == (int)IsolatedArabicLetters.Alef + || letters[index - 1] == (int)IsolatedArabicLetters.Dal + || letters[index - 1] == (int)IsolatedArabicLetters.Thal + || letters[index - 1] == (int)IsolatedArabicLetters.Ra2 + || letters[index - 1] == (int)IsolatedArabicLetters.Zeen + || letters[index - 1] == (int)IsolatedArabicLetters.PersianZe + //|| letters[index - 1] == (int)IsolatedArabicLetters.AlefMaksora + || letters[index - 1] == (int)IsolatedArabicLetters.Waw + || letters[index - 1] == (int)IsolatedArabicLetters.AlefMad + || letters[index - 1] == (int)IsolatedArabicLetters.AlefHamza + || letters[index - 1] == (int)IsolatedArabicLetters.AlefMaksoor + || letters[index - 1] == (int)IsolatedArabicLetters.WawHamza; + + bool lettersThatCannotBeALeadingLetter = letters[index] != ' ' + && letters[index] != (int)IsolatedArabicLetters.Dal + && letters[index] != (int)IsolatedArabicLetters.Thal + && letters[index] != (int)IsolatedArabicLetters.Ra2 + && letters[index] != (int)IsolatedArabicLetters.Zeen + && letters[index] != (int)IsolatedArabicLetters.PersianZe + && letters[index] != (int)IsolatedArabicLetters.Alef + && letters[index] != (int)IsolatedArabicLetters.AlefHamza + && letters[index] != (int)IsolatedArabicLetters.AlefMaksoor + && letters[index] != (int)IsolatedArabicLetters.AlefMad + && letters[index] != (int)IsolatedArabicLetters.WawHamza + && letters[index] != (int)IsolatedArabicLetters.Waw + && letters[index] != (int)IsolatedArabicLetters.Hamza; + + bool lettersThatCannotBeAfterLeadingLetter = index < letters.Length - 1 + && letters[index + 1] != ' ' + && !char.IsPunctuation(letters[index + 1]) + && !char.IsNumber(letters[index + 1]) + && !char.IsSymbol(letters[index + 1]) + && !char.IsLower(letters[index + 1]) + && !char.IsUpper(letters[index + 1]) + && letters[index + 1] != (int)IsolatedArabicLetters.Hamza; + + if (lettersThatCannotBeBeforeALeadingLetter && lettersThatCannotBeALeadingLetter && lettersThatCannotBeAfterLeadingLetter) + + // if ((index == 0 || letters[index - 1] == ' ' || letters[index - 1] == '*' || letters[index - 1] == 'A' || char.IsPunctuation(letters[index - 1]) + // || letters[index - 1] == '>' || letters[index - 1] == '<' + // || letters[index - 1] == (int)IsolatedArabicLetters.Alef + // || letters[index - 1] == (int)IsolatedArabicLetters.Dal || letters[index - 1] == (int)IsolatedArabicLetters.Thal + // || letters[index - 1] == (int)IsolatedArabicLetters.Ra2 + // || letters[index - 1] == (int)IsolatedArabicLetters.Zeen || letters[index - 1] == (int)IsolatedArabicLetters.PersianZe + // || letters[index - 1] == (int)IsolatedArabicLetters.AlefMaksora || letters[index - 1] == (int)IsolatedArabicLetters.Waw + // || letters[index - 1] == (int)IsolatedArabicLetters.AlefMad || letters[index - 1] == (int)IsolatedArabicLetters.AlefHamza + // || letters[index - 1] == (int)IsolatedArabicLetters.AlefMaksoor || letters[index - 1] == (int)IsolatedArabicLetters.WawHamza) + // && letters[index] != ' ' && letters[index] != (int)IsolatedArabicLetters.Dal + // && letters[index] != (int)IsolatedArabicLetters.Thal + // && letters[index] != (int)IsolatedArabicLetters.Ra2 + // && letters[index] != (int)IsolatedArabicLetters.Zeen && letters[index] != (int)IsolatedArabicLetters.PersianZe + // && letters[index] != (int)IsolatedArabicLetters.Alef && letters[index] != (int)IsolatedArabicLetters.AlefHamza + // && letters[index] != (int)IsolatedArabicLetters.AlefMaksoor + // && letters[index] != (int)IsolatedArabicLetters.AlefMad + // && letters[index] != (int)IsolatedArabicLetters.WawHamza + // && letters[index] != (int)IsolatedArabicLetters.Waw + // && letters[index] != (int)IsolatedArabicLetters.Hamza + // && index < letters.Length - 1 && letters[index + 1] != ' ' && !char.IsPunctuation(letters[index + 1] ) && !char.IsNumber(letters[index + 1]) + // && letters[index + 1] != (int)IsolatedArabicLetters.Hamza ) + { + return true; + } + + return false; + } + + /// + /// Checks if the letter at index value is a finishing character in Arabic or not. + /// + /// The whole word that contains the character to be checked + /// The index of the character to be checked + /// True if the character at index is a finishing character, else, returns false + internal static bool IsFinishingLetter(char[] letters, int index) + { + //bool indexZero = index != 0; + bool lettersThatCannotBeBeforeAFinishingLetter = index == 0 ? false : + letters[index - 1] != ' ' + // && char.IsDigit(letters[index-1]) + // && char.IsLower(letters[index-1]) + // && char.IsUpper(letters[index-1]) + // && char.IsNumber(letters[index-1]) + // && char.IsWhiteSpace(letters[index-1]) + // && char.IsPunctuation(letters[index-1]) + // && char.IsSymbol(letters[index-1]) + + && letters[index - 1] != (int)IsolatedArabicLetters.Dal + && letters[index - 1] != (int)IsolatedArabicLetters.Thal + && letters[index - 1] != (int)IsolatedArabicLetters.Ra2 + && letters[index - 1] != (int)IsolatedArabicLetters.Zeen + && letters[index - 1] != (int)IsolatedArabicLetters.PersianZe + //&& letters[index - 1] != (int)IsolatedArabicLetters.AlefMaksora + && letters[index - 1] != (int)IsolatedArabicLetters.Waw + && letters[index - 1] != (int)IsolatedArabicLetters.Alef + && letters[index - 1] != (int)IsolatedArabicLetters.AlefMad + && letters[index - 1] != (int)IsolatedArabicLetters.AlefHamza + && letters[index - 1] != (int)IsolatedArabicLetters.AlefMaksoor + && letters[index - 1] != (int)IsolatedArabicLetters.WawHamza + && letters[index - 1] != (int)IsolatedArabicLetters.Hamza + + + + && !char.IsPunctuation(letters[index - 1]) + && letters[index - 1] != '>' + && letters[index - 1] != '<'; + + + bool lettersThatCannotBeFinishingLetters = letters[index] != ' ' && letters[index] != (int)IsolatedArabicLetters.Hamza; + + + + + if (lettersThatCannotBeBeforeAFinishingLetter && lettersThatCannotBeFinishingLetters) + + // if (index != 0 && letters[index - 1] != ' ' && letters[index - 1] != '*' && letters[index - 1] != 'A' + // && letters[index - 1] != (int)IsolatedArabicLetters.Dal && letters[index - 1] != (int)IsolatedArabicLetters.Thal + // && letters[index - 1] != (int)IsolatedArabicLetters.Ra2 + // && letters[index - 1] != (int)IsolatedArabicLetters.Zeen && letters[index - 1] != (int)IsolatedArabicLetters.PersianZe + // && letters[index - 1] != (int)IsolatedArabicLetters.AlefMaksora && letters[index - 1] != (int)IsolatedArabicLetters.Waw + // && letters[index - 1] != (int)IsolatedArabicLetters.Alef && letters[index - 1] != (int)IsolatedArabicLetters.AlefMad + // && letters[index - 1] != (int)IsolatedArabicLetters.AlefHamza && letters[index - 1] != (int)IsolatedArabicLetters.AlefMaksoor + // && letters[index - 1] != (int)IsolatedArabicLetters.WawHamza && letters[index - 1] != (int)IsolatedArabicLetters.Hamza + // && !char.IsPunctuation(letters[index - 1]) && letters[index - 1] != '>' && letters[index - 1] != '<' + // && letters[index] != ' ' && index < letters.Length + // && letters[index] != (int)IsolatedArabicLetters.Hamza) + { + //try + //{ + // if (char.IsPunctuation(letters[index + 1])) + // return true; + // else + // return false; + //} + //catch (Exception e) + //{ + // return false; + //} + + return true; + } + //return true; + + return false; + } + + /// + /// Checks if the letter at index value is a middle character in Arabic or not. + /// + /// The whole word that contains the character to be checked + /// The index of the character to be checked + /// True if the character at index is a middle character, else, returns false + internal static bool IsMiddleLetter(char[] letters, int index) + { + bool lettersThatCannotBeMiddleLetters = index == 0 ? false : + letters[index] != (int)IsolatedArabicLetters.Alef + && letters[index] != (int)IsolatedArabicLetters.Dal + && letters[index] != (int)IsolatedArabicLetters.Thal + && letters[index] != (int)IsolatedArabicLetters.Ra2 + && letters[index] != (int)IsolatedArabicLetters.Zeen + && letters[index] != (int)IsolatedArabicLetters.PersianZe + //&& letters[index] != (int)IsolatedArabicLetters.AlefMaksora + && letters[index] != (int)IsolatedArabicLetters.Waw + && letters[index] != (int)IsolatedArabicLetters.AlefMad + && letters[index] != (int)IsolatedArabicLetters.AlefHamza + && letters[index] != (int)IsolatedArabicLetters.AlefMaksoor + && letters[index] != (int)IsolatedArabicLetters.WawHamza + && letters[index] != (int)IsolatedArabicLetters.Hamza; + + bool lettersThatCannotBeBeforeMiddleCharacters = index == 0 ? false : + letters[index - 1] != (int)IsolatedArabicLetters.Alef + && letters[index - 1] != (int)IsolatedArabicLetters.Dal + && letters[index - 1] != (int)IsolatedArabicLetters.Thal + && letters[index - 1] != (int)IsolatedArabicLetters.Ra2 + && letters[index - 1] != (int)IsolatedArabicLetters.Zeen + && letters[index - 1] != (int)IsolatedArabicLetters.PersianZe + //&& letters[index - 1] != (int)IsolatedArabicLetters.AlefMaksora + && letters[index - 1] != (int)IsolatedArabicLetters.Waw + && letters[index - 1] != (int)IsolatedArabicLetters.AlefMad + && letters[index - 1] != (int)IsolatedArabicLetters.AlefHamza + && letters[index - 1] != (int)IsolatedArabicLetters.AlefMaksoor + && letters[index - 1] != (int)IsolatedArabicLetters.WawHamza + && letters[index - 1] != (int)IsolatedArabicLetters.Hamza + && !char.IsPunctuation(letters[index - 1]) + && letters[index - 1] != '>' + && letters[index - 1] != '<' + && letters[index - 1] != ' ' + && letters[index - 1] != '*'; + + bool lettersThatCannotBeAfterMiddleCharacters = index >= letters.Length - 1 ? false : + letters[index + 1] != ' ' + && letters[index + 1] != '\r' + && letters[index + 1] != (int)IsolatedArabicLetters.Hamza + && !char.IsNumber(letters[index + 1]) + && !char.IsSymbol(letters[index + 1]) + && !char.IsPunctuation(letters[index + 1]); + if (lettersThatCannotBeAfterMiddleCharacters && lettersThatCannotBeBeforeMiddleCharacters && lettersThatCannotBeMiddleLetters) + + // if (index != 0 && letters[index] != ' ' + // && letters[index] != (int)IsolatedArabicLetters.Alef && letters[index] != (int)IsolatedArabicLetters.Dal + // && letters[index] != (int)IsolatedArabicLetters.Thal && letters[index] != (int)IsolatedArabicLetters.Ra2 + // && letters[index] != (int)IsolatedArabicLetters.Zeen && letters[index] != (int)IsolatedArabicLetters.PersianZe + // && letters[index] != (int)IsolatedArabicLetters.AlefMaksora + // && letters[index] != (int)IsolatedArabicLetters.Waw && letters[index] != (int)IsolatedArabicLetters.AlefMad + // && letters[index] != (int)IsolatedArabicLetters.AlefHamza && letters[index] != (int)IsolatedArabicLetters.AlefMaksoor + // && letters[index] != (int)IsolatedArabicLetters.WawHamza && letters[index] != (int)IsolatedArabicLetters.Hamza + // && letters[index - 1] != (int)IsolatedArabicLetters.Alef && letters[index - 1] != (int)IsolatedArabicLetters.Dal + // && letters[index - 1] != (int)IsolatedArabicLetters.Thal && letters[index - 1] != (int)IsolatedArabicLetters.Ra2 + // && letters[index - 1] != (int)IsolatedArabicLetters.Zeen && letters[index - 1] != (int)IsolatedArabicLetters.PersianZe + // && letters[index - 1] != (int)IsolatedArabicLetters.AlefMaksora + // && letters[index - 1] != (int)IsolatedArabicLetters.Waw && letters[index - 1] != (int)IsolatedArabicLetters.AlefMad + // && letters[index - 1] != (int)IsolatedArabicLetters.AlefHamza && letters[index - 1] != (int)IsolatedArabicLetters.AlefMaksoor + // && letters[index - 1] != (int)IsolatedArabicLetters.WawHamza && letters[index - 1] != (int)IsolatedArabicLetters.Hamza + // && letters[index - 1] != '>' && letters[index - 1] != '<' + // && letters[index - 1] != ' ' && letters[index - 1] != '*' && !char.IsPunctuation(letters[index - 1]) + // && index < letters.Length - 1 && letters[index + 1] != ' ' && letters[index + 1] != '\r' && letters[index + 1] != 'A' + // && letters[index + 1] != '>' && letters[index + 1] != '>' && letters[index + 1] != (int)IsolatedArabicLetters.Hamza + // ) + { + try + { + if (char.IsPunctuation(letters[index + 1])) + return false; + return true; + } + catch + { + return false; + } + //return true; + } + + return false; + } + } +} diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RTLFixer.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RTLFixer.cs.meta new file mode 100644 index 00000000..7392591b --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RTLFixer.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: cada559fcf5844047b6f6333f8a36012 +timeCreated: 1506968129 +licenseType: Store +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RegisterCallback_AllowSyncFromGoogle.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RegisterCallback_AllowSyncFromGoogle.cs new file mode 100644 index 00000000..14fb9118 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RegisterCallback_AllowSyncFromGoogle.cs @@ -0,0 +1,27 @@ +using UnityEngine; + +namespace TEngine.Localization +{ + public class RegisterCallback_AllowSyncFromGoogle : MonoBehaviour + { + public void Awake() + { + LocalizationManager.Callback_AllowSyncFromGoogle = AllowSyncFromGoogle; + } + + public void OnEnable() + { + LocalizationManager.Callback_AllowSyncFromGoogle = AllowSyncFromGoogle; + } + + public void OnDisable() + { + LocalizationManager.Callback_AllowSyncFromGoogle = null; + } + + public virtual bool AllowSyncFromGoogle(LanguageSourceData Source) + { + return true; + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RegisterCallback_AllowSyncFromGoogle.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RegisterCallback_AllowSyncFromGoogle.cs.meta new file mode 100644 index 00000000..2edf9247 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RegisterCallback_AllowSyncFromGoogle.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 893c292e5fa149cf965cc68e2d471396 +timeCreated: 1601758486 \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RegisterGlobalParameters.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RegisterGlobalParameters.cs new file mode 100644 index 00000000..6d8fc708 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RegisterGlobalParameters.cs @@ -0,0 +1,28 @@ +using UnityEngine; + +namespace TEngine.Localization +{ + + public class RegisterGlobalParameters : MonoBehaviour, ILocalizationParamsManager + { + public virtual void OnEnable() + { + if (!LocalizationManager.ParamManagers.Contains(this)) + { + LocalizationManager.ParamManagers.Add(this); + LocalizationManager.LocalizeAll(true); + } + } + + public virtual void OnDisable() + { + LocalizationManager.ParamManagers.Remove(this); + } + + public virtual string GetParameterValue( string ParamName ) + { + return null; + } + + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RegisterGlobalParameters.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RegisterGlobalParameters.cs.meta new file mode 100644 index 00000000..a3c5dbb0 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/RegisterGlobalParameters.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 31f3671f793c32f47b37480ed345e8fa +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/ResourceManager.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/ResourceManager.cs new file mode 100644 index 00000000..20a532f9 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/ResourceManager.cs @@ -0,0 +1,186 @@ +using UnityEngine; +using System.Collections.Generic; +#if UNITY_5_4_OR_NEWER +using UnityEngine.SceneManagement; +#endif + +namespace TEngine.Localization +{ + public interface IResourceManager_Bundles + { + T LoadFromBundle(string path) where T : UnityEngine.Object; + } + + public class ResourceManager : MonoBehaviour + { + #region Singleton + public static ResourceManager pInstance + { + get { + bool changed = mInstance==null; + + if (mInstance==null) + mInstance = (ResourceManager)FindObjectOfType(typeof(ResourceManager)); + + if (mInstance==null) + { + GameObject GO = new GameObject("I2ResourceManager", typeof(ResourceManager)); + GO.hideFlags = GO.hideFlags | HideFlags.HideAndDontSave; // Only hide it if this manager was autocreated + mInstance = GO.GetComponent(); + #if UNITY_5_4_OR_NEWER + SceneManager.sceneLoaded += MyOnLevelWasLoaded; + #endif + } + + if (changed && Application.isPlaying) + DontDestroyOnLoad(mInstance.gameObject); + + return mInstance; + } + } + static ResourceManager mInstance; + + #endregion + + #region Management + + public List mBundleManagers = new List(); + + #if UNITY_5_4_OR_NEWER + public static void MyOnLevelWasLoaded(UnityEngine.SceneManagement.Scene scene, LoadSceneMode mode) + #else + public void OnLevelWasLoaded() + #endif + { + pInstance.CleanResourceCache(); + LocalizationManager.UpdateSources(); + } + + #endregion + + #region Assets + + public Object[] Assets; + + // This function tries finding an asset in the Assets array, if not found it tries loading it from the Resources Folder + public T GetAsset( string Name ) where T : Object + { + T Obj = FindAsset( Name ) as T; + if (Obj!=null) + return Obj; + + return LoadFromResources( Name ); + } + + Object FindAsset( string Name ) + { + if (Assets!=null) + { + for (int i=0, imax=Assets.Length; i= 0; + } + + #endregion + + #region Resources Cache + + // This cache is kept for a few moments and then cleared + // Its meant to avoid doing several Resource.Load for the same Asset while Localizing + // (e.g. Lot of labels could be trying to Load the same Font) + readonly Dictionary mResourcesCache = new Dictionary(System.StringComparer.Ordinal); // This is used to avoid re-loading the same object from resources in the same frame + //bool mCleaningScheduled = false; + + public T LoadFromResources( string Path ) where T : Object + { + try + { + if (string.IsNullOrEmpty( Path )) + return null; + + Object Obj; + // Doing Resource.Load is very slow so we are catching the recently loaded objects + if (mResourcesCache.TryGetValue( Path, out Obj ) && Obj!=null) + { + return Obj as T; + } + + T obj = null; + + if (Path.EndsWith("]", System.StringComparison.OrdinalIgnoreCase)) // Handle sprites (Multiple) loaded from resources : "SpritePath[SpriteName]" + { + int idx = Path.LastIndexOf("[", System.StringComparison.OrdinalIgnoreCase); + int len = Path.Length - idx - 2; + string MultiSpriteName = Path.Substring(idx + 1, len); + Path = Path.Substring(0, idx); + + T[] objs = Resources.LoadAll(Path); + for (int j = 0, jmax = objs.Length; j < jmax; ++j) + if (objs[j].name.Equals(MultiSpriteName)) + { + obj = objs[j]; + break; + } + } + else + { + obj = Resources.Load(Path, typeof(T)) as T; + } + + if (obj == null) + obj = LoadFromBundle( Path ); + + if (obj!=null) + mResourcesCache[Path] = obj; + + /*if (!mCleaningScheduled) + { + Invoke("CleanResourceCache", 0.1f); + mCleaningScheduled = true; + }*/ + //if (obj==null) + //Debug.LogWarningFormat( "Unable to load {0} '{1}'", typeof( T ), Path ); + + return obj; + } + catch (System.Exception e) + { + Debug.LogErrorFormat( "Unable to load {0} '{1}'\nERROR: {2}", typeof(T), Path, e.ToString() ); + return null; + } + } + + public T LoadFromBundle(string path ) where T : Object + { + for (int i = 0, imax = mBundleManagers.Count; i < imax; ++i) + if (mBundleManagers[i]!=null) + { + var obj = mBundleManagers[i].LoadFromBundle(path); + if (obj != null) + return obj; + } + return null; + } + + public void CleanResourceCache( bool unloadResources=false ) + { + mResourcesCache.Clear(); + if (unloadResources) + Resources.UnloadUnusedAssets(); + + CancelInvoke(); + //mCleaningScheduled = false; + } + + #endregion + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/ResourceManager.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/ResourceManager.cs.meta new file mode 100644 index 00000000..9734d609 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/ResourceManager.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: f3ae68c55d3a8e44989e08d4686b3db9 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/SetLanguage.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/SetLanguage.cs new file mode 100644 index 00000000..bbc26dae --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/SetLanguage.cs @@ -0,0 +1,27 @@ +using UnityEngine; + +namespace TEngine.Localization +{ + [AddComponentMenu("I2/Localization/SetLanguage Button")] + public class SetLanguage : MonoBehaviour + { + public string _Language; + +#if UNITY_EDITOR + public LanguageSource mSource; +#endif + + void OnClick() + { + ApplyLanguage(); + } + + public void ApplyLanguage() + { + if( LocalizationManager.HasLanguage(_Language)) + { + LocalizationManager.CurrentLanguage = _Language; + } + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/SetLanguage.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/SetLanguage.cs.meta new file mode 100644 index 00000000..fe2f0f44 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/SetLanguage.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: f8d7972c568b50940a54c7f599af66c5 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/SetLanguageDropdown.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/SetLanguageDropdown.cs new file mode 100644 index 00000000..800d3358 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/SetLanguageDropdown.cs @@ -0,0 +1,43 @@ +using UnityEngine; +using UnityEngine.UI; + +namespace TEngine.Localization +{ + [AddComponentMenu("I2/Localization/SetLanguage Dropdown")] + public class SetLanguageDropdown : MonoBehaviour + { + #if UNITY_5_2 || UNITY_5_3 || UNITY_5_4_OR_NEWER + void OnEnable() + { + var dropdown = GetComponent(); + if (dropdown==null) + return; + + var currentLanguage = LocalizationManager.CurrentLanguage; + if (LocalizationManager.Sources.Count==0) LocalizationManager.UpdateSources(); + var languages = LocalizationManager.GetAllLanguages(); + + // Fill the dropdown elements + dropdown.ClearOptions(); + dropdown.AddOptions( languages ); + + dropdown.value = languages.IndexOf( currentLanguage ); + dropdown.onValueChanged.RemoveListener( OnValueChanged ); + dropdown.onValueChanged.AddListener( OnValueChanged ); + } + + + void OnValueChanged( int index ) + { + var dropdown = GetComponent(); + if (index<0) + { + index = 0; + dropdown.value = index; + } + + LocalizationManager.CurrentLanguage = dropdown.options[index].text; + } + #endif + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/SetLanguageDropdown.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/SetLanguageDropdown.cs.meta new file mode 100644 index 00000000..10b09caf --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/SetLanguageDropdown.cs.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 33897b093844df84a8e8a0258a1fb0dd +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/StringObfuscator.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/StringObfuscator.cs new file mode 100644 index 00000000..746f6212 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Core/Utils/StringObfuscator.cs @@ -0,0 +1,74 @@ +using System; +using System.Text; + +namespace TEngine.Localization +{ + // Simple String Obfucator + // (not particularly safe, but will stop most players from hacking your strings and its FAST) + + public class StringObfucator + { + // Change this for your projects if you need extra security + public static char[] StringObfuscatorPassword = "ÝúbUu¸CÁ§*4PÚ©-᩾@T6Dl±ÒWâuzÅm4GÐóØ$=Íg,¥Që®iKEßr¡×60Ít4öÃ~^«y:Èd1 + /// 默认本地化辅助器。 + /// + public class DefaultLocalizationHelper + { +#if UNITY_EDITOR + public const string I2GlobalSourcesEditorPath = "Assets/Editor/I2Localization/I2Languages.asset"; +#endif + + public const string I2ResAssetNamePrefix = "I2_"; + + /// + /// 获取系统语言。 + /// + public static Language SystemLanguage + { + get + { + switch (Application.systemLanguage) + { + case UnityEngine.SystemLanguage.Afrikaans: return Language.Afrikaans; + case UnityEngine.SystemLanguage.Arabic: return Language.Arabic; + case UnityEngine.SystemLanguage.Basque: return Language.Basque; + case UnityEngine.SystemLanguage.Belarusian: return Language.Belarusian; + case UnityEngine.SystemLanguage.Bulgarian: return Language.Bulgarian; + case UnityEngine.SystemLanguage.Catalan: return Language.Catalan; + case UnityEngine.SystemLanguage.Chinese: return Language.ChineseSimplified; + case UnityEngine.SystemLanguage.ChineseSimplified: return Language.ChineseSimplified; + case UnityEngine.SystemLanguage.ChineseTraditional: return Language.ChineseTraditional; + case UnityEngine.SystemLanguage.Czech: return Language.Czech; + case UnityEngine.SystemLanguage.Danish: return Language.Danish; + case UnityEngine.SystemLanguage.Dutch: return Language.Dutch; + case UnityEngine.SystemLanguage.English: return Language.English; + case UnityEngine.SystemLanguage.Estonian: return Language.Estonian; + case UnityEngine.SystemLanguage.Faroese: return Language.Faroese; + case UnityEngine.SystemLanguage.Finnish: return Language.Finnish; + case UnityEngine.SystemLanguage.French: return Language.French; + case UnityEngine.SystemLanguage.German: return Language.German; + case UnityEngine.SystemLanguage.Greek: return Language.Greek; + case UnityEngine.SystemLanguage.Hebrew: return Language.Hebrew; + case UnityEngine.SystemLanguage.Hungarian: return Language.Hungarian; + case UnityEngine.SystemLanguage.Icelandic: return Language.Icelandic; + case UnityEngine.SystemLanguage.Indonesian: return Language.Indonesian; + case UnityEngine.SystemLanguage.Italian: return Language.Italian; + case UnityEngine.SystemLanguage.Japanese: return Language.Japanese; + case UnityEngine.SystemLanguage.Korean: return Language.Korean; + case UnityEngine.SystemLanguage.Latvian: return Language.Latvian; + case UnityEngine.SystemLanguage.Lithuanian: return Language.Lithuanian; + case UnityEngine.SystemLanguage.Norwegian: return Language.Norwegian; + case UnityEngine.SystemLanguage.Polish: return Language.Polish; + case UnityEngine.SystemLanguage.Portuguese: return Language.PortuguesePortugal; + case UnityEngine.SystemLanguage.Romanian: return Language.Romanian; + case UnityEngine.SystemLanguage.Russian: return Language.Russian; + case UnityEngine.SystemLanguage.SerboCroatian: return Language.SerboCroatian; + case UnityEngine.SystemLanguage.Slovak: return Language.Slovak; + case UnityEngine.SystemLanguage.Slovenian: return Language.Slovenian; + case UnityEngine.SystemLanguage.Spanish: return Language.Spanish; + case UnityEngine.SystemLanguage.Swedish: return Language.Swedish; + case UnityEngine.SystemLanguage.Thai: return Language.Thai; + case UnityEngine.SystemLanguage.Turkish: return Language.Turkish; + case UnityEngine.SystemLanguage.Ukrainian: return Language.Ukrainian; + case UnityEngine.SystemLanguage.Unknown: return Language.Unspecified; + case UnityEngine.SystemLanguage.Vietnamese: return Language.Vietnamese; + default: return Language.Unspecified; + } + } + } + + private static readonly Dictionary s_LanguageMap = new Dictionary(); + private static readonly Dictionary s_LanguageStrMap = new Dictionary(); + + static DefaultLocalizationHelper() + { + RegisterLanguageMap(Language.English); + RegisterLanguageMap(Language.ChineseSimplified, "Chinese"); + RegisterLanguageMap(Language.ChineseTraditional); + RegisterLanguageMap(Language.Japanese); + RegisterLanguageMap(Language.Korean); + } + + private static void RegisterLanguageMap(Language language, string str = "") + { + if (string.IsNullOrEmpty(str)) + { + str = language.ToString(); + } + + s_LanguageMap[language] = str; + s_LanguageStrMap[str] = language; + } + + /// + /// 根据语言字符串获取语言枚举。 + /// + /// 语言字符串。 + /// 语言枚举。 + public static Language GetLanguage(string str) + { + if (string.IsNullOrEmpty(str)) + { + return Language.Unspecified; + } + + if (s_LanguageStrMap.TryGetValue(str, out var language)) + { + return language; + } + + language = Language.English; + return language; + } + + /// + /// 根据语言枚举获取语言字符串。 + /// + /// 语言枚举。 + /// 语言字符串。 + public static string GetLanguageStr(Language language) + { + if (s_LanguageMap.TryGetValue(language, out var ret)) + { + return ret; + } + + ret = "English"; + return ret; + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/DefaultLocalizationHelper.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/DefaultLocalizationHelper.cs.meta new file mode 100644 index 00000000..86e3ed7e --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/DefaultLocalizationHelper.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 57465d541030d834b9d764c79af94e2e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Language.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Language.cs.meta index 10301266..5de8e74d 100644 --- a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Language.cs.meta +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/Language.cs.meta @@ -1,3 +1,3 @@ fileFormatVersion: 2 -guid: c8504b3313af475386e7dfb47b2cc63b -timeCreated: 1734400347 \ No newline at end of file +guid: 8ab44b0472a74109b0a0c1eba66eeb15 +timeCreated: 1710742688 \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/LocalizationModule.cs b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/LocalizationModule.cs new file mode 100644 index 00000000..78b8a20d --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/LocalizationModule.cs @@ -0,0 +1,329 @@ +using UnityEngine; +using Object = UnityEngine.Object; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using Cysharp.Threading.Tasks; +using TEngine.Localization; +using UnityEngine.Serialization; + +namespace TEngine +{ + /// + /// 本地化组件。 + /// + [DisallowMultipleComponent] + public sealed class LocalizationModule : MonoBehaviour, IResourceManager_Bundles + { + #region GetInstance + + private static LocalizationModule _instance = null; + + public static LocalizationModule Instance + { + get + { + if (_instance == null) + { + _instance = FindObjectOfType(); + } + + return _instance; + } + } + + #endregion + + private string _defaultLanguage = "Chinese"; + + [SerializeField] + private TextAsset innerLocalizationCsv; + + private LanguageSource _languageSource; + + private LanguageSourceData _sourceData + { + get + { + if (_languageSource == null) + { + _languageSource = gameObject.AddComponent(); + } + + return _languageSource.SourceData; + } + } + + [SerializeField] + private List allLanguage = new List(); + + /// + /// 模拟平台运行时 编辑器资源不加载。 + /// + [SerializeField] + private bool useRuntimeModule = true; + + private string _currentLanguage; + + /// + /// 获取或设置本地化语言。 + /// + public Language Language + { + get => DefaultLocalizationHelper.GetLanguage(_currentLanguage); + set => SetLanguage(DefaultLocalizationHelper.GetLanguageStr(value)); + } + + /// + /// 获取系统语言。 + /// + public Language SystemLanguage => DefaultLocalizationHelper.SystemLanguage; + + private IResourceModule _resourceModule; + + /// + /// 游戏框架组件初始化。 + /// + private void Awake() + { + _instance = this; + _resourceModule = ModuleSystem.GetModule(); + if (_resourceModule == null) + { + Log.Fatal("Resource component is invalid."); + return; + } + } + + private void Start() + { + RootModule rootModule = RootModule.Instance; + if (rootModule == null) + { + Log.Fatal("Base component is invalid."); + return; + } + + _defaultLanguage = DefaultLocalizationHelper.GetLanguageStr( + rootModule.EditorLanguage != Language.Unspecified ? rootModule.EditorLanguage : SystemLanguage); + + AsyncInit().Forget(); + } + + private async UniTask AsyncInit() + { + if (string.IsNullOrEmpty(_defaultLanguage)) + { + Log.Fatal($"Must set defaultLanguage."); + return false; + } +#if UNITY_EDITOR + if (!useRuntimeModule) + { + LocalizationManager.RegisterSourceInEditor(); + UpdateAllLanguages(); + SetLanguage(_defaultLanguage); + } + else + { + _sourceData.Awake(); + await LoadLanguage(_defaultLanguage, true, true); + } +#else + _sourceData.Awake(); + await LoadLanguage(_defaultLanguage, true, true); +#endif + return true; + } + + /// + /// 加载语言总表。 + /// + public async UniTask LoadLanguageTotalAsset(string assetName) + { +#if UNITY_EDITOR + if (!useRuntimeModule) + { + Log.Warning($"禁止在此模式下 动态加载语言"); + return; + } +#endif + TextAsset assetTextAsset = await _resourceModule.LoadAssetAsync(assetName); + + if (assetTextAsset == null) + { + Log.Warning($"没有加载到语言总表"); + return; + } + + Log.Info($"加载语言总表成功"); + + UseLocalizationCSV(assetTextAsset.text, true); + } + + /// + /// 加载语言分表。 + /// + /// 语言类型。 + /// 是否立刻设置成当前语言。 + /// 是否初始化Inner语言。 + public async UniTask LoadLanguage(string language, bool setCurrent = false, bool fromInit = false) + { +#if UNITY_EDITOR + if (!useRuntimeModule) + { + Log.Warning($"禁止在此模式下 动态加载语言 {language}"); + return; + } +#endif + TextAsset assetTextAsset; + + if (!fromInit) + { + var assetName = GetLanguageAssetName(language); + + assetTextAsset = await _resourceModule.LoadAssetAsync(assetName); + } + else + { + if (innerLocalizationCsv == null) + { + Log.Warning($"请使用I2Localization.asset导出CSV创建内置多语言."); + return; + } + + assetTextAsset = innerLocalizationCsv; + } + + if (assetTextAsset == null) + { + Log.Warning($"没有加载到目标语言资源 {language}"); + return; + } + + Log.Info($"加载语言成功 {language}"); + + UseLocalizationCSV(assetTextAsset.text, !setCurrent); + if (setCurrent) + { + SetLanguage(language); + } + } + + private string GetLanguageAssetName(string language) + { + return $"{DefaultLocalizationHelper.I2ResAssetNamePrefix}{language}"; + } + + /// + /// 检查并初始化所有语言的Id。 + /// + private void UpdateAllLanguages() + { + this.allLanguage.Clear(); + List allLanguages = LocalizationManager.GetAllLanguages(); + foreach (var language in allLanguages) + { + var newLanguage = Regex.Replace(language, @"[\r\n]", ""); + this.allLanguage.Add(newLanguage); + } + } + + /// + /// 检查是否存在该语言。 + /// + /// 语言。 + /// 是否已加载。 + public bool CheckLanguage(string language) + { + return allLanguage.Contains(language); + } + + /// + /// 设置当前语言。 + /// + /// 语言名称。 + /// 是否加载。 + /// + public bool SetLanguage(Language language, bool load = false) + { + return SetLanguage(DefaultLocalizationHelper.GetLanguageStr(language), load); + } + + /// + /// 设置当前语言。 + /// + /// 语言名称。 + /// 是否加载。 + /// + public bool SetLanguage(string language, bool load = false) + { + if (!CheckLanguage(language)) + { + if (load) + { + LoadLanguage(language, true).Forget(); + return true; + } + + Log.Warning($"当前没有这个语言无法切换到此语言 {language}"); + return false; + } + + if (_currentLanguage == language) + { + return true; + } + + Log.Info($"设置当前语言 = {language}"); + LocalizationManager.CurrentLanguage = language; + _currentLanguage = language; + return true; + } + + /// + /// 通过语言的Id设置语言。 + /// + /// 语言ID。 + /// 是否设置成功。 + public bool SetLanguage(int languageId) + { + if (languageId < 0 || languageId >= allLanguage.Count) + { + Log.Warning($"Error languageIndex. Could not set and check {languageId} Language.Count = {allLanguage.Count}."); + return false; + } + + var language = allLanguage[languageId]; + return SetLanguage(language); + } + + private void UseLocalizationCSV(string text, bool isLocalizeAll = false) + { + _sourceData.Import_CSV(string.Empty, text, eSpreadsheetUpdateMode.Merge, ','); + if (isLocalizeAll) + { + LocalizationManager.LocalizeAll(); + } + + UpdateAllLanguages(); + } + + /// + /// 语言模块加载资源接口。 + /// + /// 资源定位地址。 + /// 资源类型。 + /// 返回资源实例。 + public T LoadFromBundle(string path) where T : Object + { + var assetObject = _resourceModule.LoadAsset(path); + if (assetObject != null) + { + return assetObject; + } + + Log.Error($"Localization could not load {path} assetsType :{typeof(T).Name}."); + return null; + } + } +} \ No newline at end of file diff --git a/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/LocalizationModule.cs.meta b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/LocalizationModule.cs.meta new file mode 100644 index 00000000..4a6e0972 --- /dev/null +++ b/UnityProject/Assets/TEngine/Runtime/Module/LocalizationModule/LocalizationModule.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 706e6317a59f61044b2805be79f6b284 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/UnityProject/Assets/TEngine/Runtime/Module/RootModule.cs b/UnityProject/Assets/TEngine/Runtime/Module/RootModule.cs index 72e55972..cd73eae0 100644 --- a/UnityProject/Assets/TEngine/Runtime/Module/RootModule.cs +++ b/UnityProject/Assets/TEngine/Runtime/Module/RootModule.cs @@ -9,6 +9,20 @@ namespace TEngine [DisallowMultipleComponent] public sealed class RootModule : MonoBehaviour { + private static RootModule _instance = null; + + public static RootModule Instance + { + get + { + if (_instance == null) + { + _instance = FindObjectOfType(); + } + return _instance; + } + } + private const int DEFAULT_DPI = 96; // default windows dpi private float _gameSpeedBeforePause = 1f; @@ -101,6 +115,7 @@ namespace TEngine /// private void Awake() { + _instance = this; InitTextHelper(); InitLogHelper(); Log.Info("Unity Version: {0}", Application.unityVersion); diff --git a/UnityProject/Assets/TEngine/Settings/Prefab/GameEntry.prefab b/UnityProject/Assets/TEngine/Settings/Prefab/GameEntry.prefab index 2fc9e576..19a52cc1 100644 --- a/UnityProject/Assets/TEngine/Settings/Prefab/GameEntry.prefab +++ b/UnityProject/Assets/TEngine/Settings/Prefab/GameEntry.prefab @@ -1,5 +1,52 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: +--- !u!1 &4499406540586983459 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 404609667950854598} + - component: {fileID: 6697614230421034925} + m_Layer: 0 + m_Name: LocalizationModule + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &404609667950854598 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4499406540586983459} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 7696541955266276700} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &6697614230421034925 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4499406540586983459} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 706e6317a59f61044b2805be79f6b284, type: 3} + m_Name: + m_EditorClassIdentifier: + innerLocalizationCsv: {fileID: 0} + allLanguage: [] + useRuntimeModule: 1 --- !u!1 &7696541953913300370 GameObject: m_ObjectHideFlags: 0 @@ -44,26 +91,26 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: d58978a9ed0f473ca3cc5a302c3bf87b, type: 3} m_Name: m_EditorClassIdentifier: - m_Skin: {fileID: 11400000, guid: b38f904eb09e4bd48a46a63750942109, type: 2} - m_ActiveWindow: 0 - m_ShowFullWindow: 0 - m_ConsoleWindow: - m_LockScroll: 1 - m_MaxLine: 100 - m_InfoFilter: 1 - m_WarningFilter: 1 - m_ErrorFilter: 1 - m_FatalFilter: 1 - m_InfoColor: + skin: {fileID: 0} + activeWindow: 0 + _showFullWindow: 0 + _consoleWindow: + lockScroll: 1 + maxLine: 100 + infoFilter: 1 + warningFilter: 1 + errorFilter: 1 + fatalFilter: 1 + infoColor: serializedVersion: 2 rgba: 4294967295 - m_WarningColor: + warningColor: serializedVersion: 2 rgba: 4278512639 - m_ErrorColor: + errorColor: serializedVersion: 2 rgba: 4278190335 - m_FatalColor: + fatalColor: serializedVersion: 2 rgba: 4281545650 --- !u!1 &7696541954382622705 @@ -156,19 +203,19 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: da56eb8e282380a478681f0d4445502d, type: 3} m_Name: m_EditorClassIdentifier: - m_MinUnloadUnusedAssetsInterval: 60 - m_MaxUnloadUnusedAssetsInterval: 300 - m_UseSystemUnloadUnusedAssets: 1 + minUnloadUnusedAssetsInterval: 60 + maxUnloadUnusedAssetsInterval: 300 + useSystemUnloadUnusedAssets: 1 packageName: DefaultPackage playMode: 0 - m_UpdatableWhilePlaying: 0 - Milliseconds: 30 - m_DownloadingMaxNum: 10 - m_FailedTryAgain: 3 - m_AssetAutoReleaseInterval: 60 - m_AssetCapacity: 64 - m_AssetExpireTime: 60 - m_AssetPriority: 0 + updatableWhilePlaying: 0 + milliseconds: 30 + downloadingMaxNum: 10 + failedTryAgain: 3 + assetAutoReleaseInterval: 60 + assetCapacity: 64 + assetExpireTime: 60 + assetPriority: 0 --- !u!114 &7696541955235861476 MonoBehaviour: m_ObjectHideFlags: 0 @@ -181,8 +228,8 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 6153d27eaaa74c0e9a00c9eb273cc21c, type: 3} m_Name: m_EditorClassIdentifier: - m_CheckCanReleaseInterval: 30 - m_AutoReleaseInterval: 60 + checkCanReleaseInterval: 30 + autoReleaseInterval: 60 --- !u!1 &7696541955266276702 GameObject: m_ObjectHideFlags: 0 @@ -217,6 +264,7 @@ Transform: - {fileID: 7696541954382622710} - {fileID: 7696541955418391618} - {fileID: 7696541955235861479} + - {fileID: 404609667950854598} m_Father: {fileID: 0} m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} @@ -244,14 +292,14 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 72e5f3a6e6b245989e960d072a9323ce, type: 3} m_Name: m_EditorClassIdentifier: - m_EditorLanguage: 0 - m_TextHelperTypeName: TEngine.DefaultTextHelper - m_LogHelperTypeName: TEngine.DefaultLogHelper - m_JsonHelperTypeName: TEngine.DefaultJsonHelper - m_FrameRate: 120 - m_GameSpeed: 1 - m_RunInBackground: 1 - m_NeverSleep: 1 + editorLanguage: 0 + textHelperTypeName: TEngine.DefaultTextHelper + logHelperTypeName: TEngine.DefaultLogHelper + jsonHelperTypeName: TEngine.DefaultJsonHelper + frameRate: 120 + gameSpeed: 1 + runInBackground: 1 + neverSleep: 1 --- !u!1 &7696541955418391628 GameObject: m_ObjectHideFlags: 0