aboutsummaryrefslogtreecommitdiff
path: root/Assets/Packages/Lean/Localization/Scripts
diff options
context:
space:
mode:
authorAndrew Lee <alee14498@gmail.com>2019-08-24 15:24:57 -0400
committerAndrew Lee <alee14498@gmail.com>2019-08-24 15:24:57 -0400
commit85553832ead1a96f88726cd2b35cb6ff1d8b8cc8 (patch)
tree7a2615034462d4296ed09d24038bb4c68107979d /Assets/Packages/Lean/Localization/Scripts
parente06acf066171670248b0b644c0eb8f6d895e051e (diff)
downloadUnicity-85553832ead1a96f88726cd2b35cb6ff1d8b8cc8.tar.gz
Unicity-85553832ead1a96f88726cd2b35cb6ff1d8b8cc8.tar.bz2
Unicity-85553832ead1a96f88726cd2b35cb6ff1d8b8cc8.zip
Attempt number 2 on localization
Diffstat (limited to 'Assets/Packages/Lean/Localization/Scripts')
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours.meta10
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedAudioSource.cs47
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedAudioSource.cs.meta8
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedImage.cs48
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedImage.cs.meta8
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedRenderer.cs55
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedRenderer.cs.meta12
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedSpriteRenderer.cs47
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedSpriteRenderer.cs.meta8
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedText.cs48
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedText.cs.meta8
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextFont.cs48
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextFont.cs.meta12
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMesh.cs46
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMesh.cs.meta13
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMeshFont.cs46
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMeshFont.cs.meta13
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanLanguage.cs43
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanLanguage.cs.meta8
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanLanguageCSV.cs303
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanLanguageCSV.cs.meta8
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanLanguageNameAttribute.cs46
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanLanguageNameAttribute.cs.meta8
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanLocalization.cs1016
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanLocalization.cs.meta8
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanLocalizedBehaviour.cs121
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanLocalizedBehaviour.cs.meta8
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanPhrase.cs299
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanPhrase.cs.meta8
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanPrefab.cs176
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanPrefab.cs.meta8
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanSource.cs30
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanSource.cs.meta8
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanToken.cs130
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanToken.cs.meta8
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanTranslation.cs191
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanTranslation.cs.meta8
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanTranslationNameAttribute.cs54
-rw-r--r--Assets/Packages/Lean/Localization/Scripts/LeanTranslationNameAttribute.cs.meta8
39 files changed, 2974 insertions, 0 deletions
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours.meta b/Assets/Packages/Lean/Localization/Scripts/Behaviours.meta
new file mode 100644
index 0000000..f442d8e
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: dfd423fd99d68f647853387ae5466692
+folderAsset: yes
+timeCreated: 1555734116
+licenseType: Store
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedAudioSource.cs b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedAudioSource.cs
new file mode 100644
index 0000000..7b45595
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedAudioSource.cs
@@ -0,0 +1,47 @@
+using UnityEngine;
+
+namespace Lean.Localization
+{
+ /// <summary>This component will update an AudioSource component with localized text, or use a fallback if none is found.</summary>
+ [ExecuteInEditMode]
+ [DisallowMultipleComponent]
+ [RequireComponent(typeof(AudioSource))]
+ [HelpURL(LeanLocalization.HelpUrlPrefix + "LeanLocalizedAudioSource")]
+ [AddComponentMenu(LeanLocalization.ComponentPathPrefix + "Localized AudioSource")]
+ public class LeanLocalizedAudioSource : LeanLocalizedBehaviour
+ {
+ [Tooltip("If PhraseName couldn't be found, this clip will be used")]
+ public AudioClip FallbackAudioClip;
+
+ // This gets called every time the translation needs updating
+ public override void UpdateTranslation(LeanTranslation translation)
+ {
+ // Get the AudioSource component attached to this GameObject
+ var audioSource = GetComponent<AudioSource>();
+
+ // Use translation?
+ if (translation != null && translation.Data is AudioClip)
+ {
+ audioSource.clip = (AudioClip)translation.Data;
+ }
+ // Use fallback?
+ else
+ {
+ audioSource.clip = FallbackAudioClip;
+ }
+ }
+
+ protected virtual void Awake()
+ {
+ // Should we set FallbackAudioClip?
+ if (FallbackAudioClip == null)
+ {
+ // Get the AudioSource component attached to this GameObject
+ var audioSource = GetComponent<AudioSource>();
+
+ // Copy current sprite to fallback
+ FallbackAudioClip = audioSource.clip;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedAudioSource.cs.meta b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedAudioSource.cs.meta
new file mode 100644
index 0000000..479d2ca
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedAudioSource.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4bbcd1203ed53d54e9a127af382c0181
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedImage.cs b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedImage.cs
new file mode 100644
index 0000000..664ec95
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedImage.cs
@@ -0,0 +1,48 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+namespace Lean.Localization
+{
+ /// <summary>This component will update an Image component with a localized sprite, or use a fallback if none is found</summary>
+ [ExecuteInEditMode]
+ [DisallowMultipleComponent]
+ [RequireComponent(typeof(Image))]
+ [HelpURL(LeanLocalization.HelpUrlPrefix + "LeanLocalizedImage")]
+ [AddComponentMenu(LeanLocalization.ComponentPathPrefix + "Localized Image")]
+ public class LeanLocalizedImage : LeanLocalizedBehaviour
+ {
+ [Tooltip("If PhraseName couldn't be found, this sprite will be used")]
+ public Sprite FallbackSprite;
+
+ // This gets called every time the translation needs updating
+ public override void UpdateTranslation(LeanTranslation translation)
+ {
+ // Get the Image component attached to this GameObject
+ var image = GetComponent<Image>();
+
+ // Use translation?
+ if (translation != null && translation.Data is Sprite)
+ {
+ image.sprite = (Sprite)translation.Data;
+ }
+ // Use fallback?
+ else
+ {
+ image.sprite = FallbackSprite;
+ }
+ }
+
+ protected virtual void Awake()
+ {
+ // Should we set FallbackSprite?
+ if (FallbackSprite == null)
+ {
+ // Get the SpriteRenderer component attached to this GameObject
+ var spriteRenderer = GetComponent<Image>();
+
+ // Copy current sprite to fallback
+ FallbackSprite = spriteRenderer.sprite;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedImage.cs.meta b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedImage.cs.meta
new file mode 100644
index 0000000..b7be84d
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedImage.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 5b28f7e33d1e3e0408d7ff2c8a92a650
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedRenderer.cs b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedRenderer.cs
new file mode 100644
index 0000000..701e538
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedRenderer.cs
@@ -0,0 +1,55 @@
+using UnityEngine;
+
+namespace Lean.Localization
+{
+ /// <summary>This component will update a Renderer component's sharedMaterial with a localized material, or use a fallback if none is found.</summary>
+ [ExecuteInEditMode]
+ [DisallowMultipleComponent]
+ [RequireComponent(typeof(Renderer))]
+ [HelpURL(LeanLocalization.HelpUrlPrefix + "LeanLocalizedRenderer")]
+ [AddComponentMenu(LeanLocalization.ComponentPathPrefix + "Localized Renderer")]
+ public class LeanLocalizedRenderer : LeanLocalizedBehaviour
+ {
+ [Tooltip("If PhraseName couldn't be found, this material will be used")]
+ public Material FallbackMaterial;
+
+ [Tooltip("The material index you want to replace.")]
+ public int Index;
+
+ // This gets called every time the translation needs updating
+ public override void UpdateTranslation(LeanTranslation translation)
+ {
+ // Get the Renderer component attached to this GameObject
+ var renderer = GetComponent<Renderer>();
+
+ // Get the shared materials of this component
+ var sharedMaterials = renderer.sharedMaterials;
+
+ // Use translation?
+ if (translation != null && translation.Data is Material)
+ {
+ sharedMaterials[Index] = (Material)translation.Data;
+ }
+ // Use fallback?
+ else
+ {
+ sharedMaterials[Index] = FallbackMaterial;
+ }
+
+ renderer.sharedMaterials = sharedMaterials;
+ }
+
+ protected virtual void Awake()
+ {
+ // Should we set FallbackFont?
+ if (FallbackMaterial == null)
+ {
+ // Get the Renderer component attached to this GameObject
+ var renderer = GetComponent<Renderer>();
+
+ // Copy current material to fallback
+ FallbackMaterial = renderer.sharedMaterials[Index];
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedRenderer.cs.meta b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedRenderer.cs.meta
new file mode 100644
index 0000000..4fcb5ed
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedRenderer.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: b42c83c3c333a7e4a8e498fec50ce1fb
+timeCreated: 1478133615
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedSpriteRenderer.cs b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedSpriteRenderer.cs
new file mode 100644
index 0000000..e1170b0
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedSpriteRenderer.cs
@@ -0,0 +1,47 @@
+using UnityEngine;
+
+namespace Lean.Localization
+{
+ /// <summary>This component will update a SpriteRenderer component with a localized sprite, or use a fallback if none is found</summary>
+ [ExecuteInEditMode]
+ [DisallowMultipleComponent]
+ [RequireComponent(typeof(SpriteRenderer))]
+ [HelpURL(LeanLocalization.HelpUrlPrefix + "LeanLocalizedSpriteRenderer")]
+ [AddComponentMenu(LeanLocalization.ComponentPathPrefix + "Localized SpriteRenderer")]
+ public class LeanLocalizedSpriteRenderer : LeanLocalizedBehaviour
+ {
+ [Tooltip("If PhraseName couldn't be found, this sprite will be used")]
+ public Sprite FallbackSprite;
+
+ // This gets called every time the translation needs updating
+ public override void UpdateTranslation(LeanTranslation translation)
+ {
+ // Get the SpriteRenderer component attached to this GameObject
+ var spriteRenderer = GetComponent<SpriteRenderer>();
+
+ // Use translation?
+ if (translation != null && translation.Data is Sprite)
+ {
+ spriteRenderer.sprite = (Sprite)translation.Data;
+ }
+ // Use fallback?
+ else
+ {
+ spriteRenderer.sprite = FallbackSprite;
+ }
+ }
+
+ protected virtual void Awake()
+ {
+ // Should we set FallbackSprite?
+ if (FallbackSprite == null)
+ {
+ // Get the SpriteRenderer component attached to this GameObject
+ var spriteRenderer = GetComponent<SpriteRenderer>();
+
+ // Copy current sprite to fallback
+ FallbackSprite = spriteRenderer.sprite;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedSpriteRenderer.cs.meta b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedSpriteRenderer.cs.meta
new file mode 100644
index 0000000..84485b2
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedSpriteRenderer.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 84f599809c9879044b69f17af8c248c6
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedText.cs b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedText.cs
new file mode 100644
index 0000000..df63743
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedText.cs
@@ -0,0 +1,48 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+namespace Lean.Localization
+{
+ /// <summary>This component will update a UI.Text component with localized text, or use a fallback if none is found.</summary>
+ [ExecuteInEditMode]
+ [DisallowMultipleComponent]
+ [RequireComponent(typeof(Text))]
+ [HelpURL(LeanLocalization.HelpUrlPrefix + "LeanLocalizedText")]
+ [AddComponentMenu(LeanLocalization.ComponentPathPrefix + "Localized Text")]
+ public class LeanLocalizedText : LeanLocalizedBehaviour
+ {
+ [Tooltip("If PhraseName couldn't be found, this text will be used")]
+ public string FallbackText;
+
+ // This gets called every time the translation needs updating
+ public override void UpdateTranslation(LeanTranslation translation)
+ {
+ // Get the Text component attached to this GameObject
+ var text = GetComponent<Text>();
+
+ // Use translation?
+ if (translation != null && translation.Data is string)
+ {
+ text.text = LeanTranslation.FormatText((string)translation.Data, text.text, this);
+ }
+ // Use fallback?
+ else
+ {
+ text.text = LeanTranslation.FormatText(FallbackText, text.text, this);
+ }
+ }
+
+ protected virtual void Awake()
+ {
+ // Should we set FallbackText?
+ if (string.IsNullOrEmpty(FallbackText) == true)
+ {
+ // Get the Text component attached to this GameObject
+ var text = GetComponent<Text>();
+
+ // Copy current text to fallback
+ FallbackText = text.text;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedText.cs.meta b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedText.cs.meta
new file mode 100644
index 0000000..8b76311
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedText.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 7ee4021ce59f313499a5cab041f02aba
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextFont.cs b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextFont.cs
new file mode 100644
index 0000000..6730956
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextFont.cs
@@ -0,0 +1,48 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+namespace Lean.Localization
+{
+ /// <summary>This component will update a Text component's Font with a localized font, or use a fallback if none is found.</summary>
+ [ExecuteInEditMode]
+ [DisallowMultipleComponent]
+ [RequireComponent(typeof(Text))]
+ [HelpURL(LeanLocalization.HelpUrlPrefix + "LeanLocalizedTextFont")]
+ [AddComponentMenu(LeanLocalization.ComponentPathPrefix + "Localized TextFont")]
+ public class LeanLocalizedTextFont : LeanLocalizedBehaviour
+ {
+ [Tooltip("If PhraseName couldn't be found, this font will be used")]
+ public Font FallbackFont;
+
+ // This gets called every time the translation needs updating
+ public override void UpdateTranslation(LeanTranslation translation)
+ {
+ // Get the Text component attached to this GameObject
+ var text = GetComponent<Text>();
+
+ // Use translation?
+ if (translation != null && translation.Data is Font)
+ {
+ text.font = (Font)translation.Data;
+ }
+ // Use fallback?
+ else
+ {
+ text.font = FallbackFont;
+ }
+ }
+
+ protected virtual void Awake()
+ {
+ // Should we set FallbackFont?
+ if (FallbackFont == null)
+ {
+ // Get the Text component attached to this GameObject
+ var text = GetComponent<Text>();
+
+ // Copy current font to fallback
+ FallbackFont = text.font;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextFont.cs.meta b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextFont.cs.meta
new file mode 100644
index 0000000..5b0ceea
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextFont.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: fce53ba05f9c84246b062afd12f4619f
+timeCreated: 1478133615
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMesh.cs b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMesh.cs
new file mode 100644
index 0000000..c9fcbc6
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMesh.cs
@@ -0,0 +1,46 @@
+using UnityEngine;
+
+namespace Lean.Localization
+{
+ /// <summary>This component will update a TMPro.TextMeshProUGUI component with localized text, or use a fallback if none is found.</summary>
+ [ExecuteInEditMode]
+ [DisallowMultipleComponent]
+ [RequireComponent(typeof(TextMesh))]
+ [AddComponentMenu(LeanLocalization.ComponentPathPrefix + "Localized TextMesh")]
+ public class LeanLocalizedTextMesh : LeanLocalizedBehaviour
+ {
+ [Tooltip("If PhraseName couldn't be found, this text will be used")]
+ public string FallbackText;
+
+ // This gets called every time the translation needs updating
+ public override void UpdateTranslation(LeanTranslation translation)
+ {
+ // Get the TextMeshProUGUI component attached to this GameObject
+ var text = GetComponent<TextMesh>();
+
+ // Use translation?
+ if (translation != null && translation.Data is string)
+ {
+ text.text = LeanTranslation.FormatText((string)translation.Data, text.text, this);
+ }
+ // Use fallback?
+ else
+ {
+ text.text = LeanTranslation.FormatText(FallbackText, text.text, this);
+ }
+ }
+
+ protected virtual void Awake()
+ {
+ // Should we set FallbackText?
+ if (string.IsNullOrEmpty(FallbackText) == true)
+ {
+ // Get the TextMeshProUGUI component attached to this GameObject
+ var text = GetComponent<TextMesh>();
+
+ // Copy current text to fallback
+ FallbackText = text.text;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMesh.cs.meta b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMesh.cs.meta
new file mode 100644
index 0000000..6af3498
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMesh.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 365897690d9e7c54f9fd65401bffb0d3
+timeCreated: 1561293177
+licenseType: Store
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMeshFont.cs b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMeshFont.cs
new file mode 100644
index 0000000..a0046a7
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMeshFont.cs
@@ -0,0 +1,46 @@
+using UnityEngine;
+
+namespace Lean.Localization
+{
+ /// <summary>This component will update a TextMesh component's Font with a localized font, or use a fallback if none is found.</summary>
+ [ExecuteInEditMode]
+ [DisallowMultipleComponent]
+ [RequireComponent(typeof(TextMesh))]
+ [AddComponentMenu(LeanLocalization.ComponentPathPrefix + "Localized TextMesh Font")]
+ public class LeanLocalizedTextMeshFont : LeanLocalizedBehaviour
+ {
+ [Tooltip("If PhraseName couldn't be found, this font asset will be used")]
+ public Font FallbackFont;
+
+ // This gets called every time the translation needs updating
+ public override void UpdateTranslation(LeanTranslation translation)
+ {
+ // Get the TextMesh component attached to this GameObject
+ var text = GetComponent<TextMesh>();
+
+ // Use translation?
+ if (translation != null && translation.Data is Font)
+ {
+ text.font = (Font)translation.Data;
+ }
+ // Use fallback?
+ else
+ {
+ text.font = FallbackFont;
+ }
+ }
+
+ protected virtual void Awake()
+ {
+ // Should we set FallbackFont?
+ if (FallbackFont == null)
+ {
+ // Get the TextMesh component attached to this GameObject
+ var text = GetComponent<TextMesh>();
+
+ // Copy current text to fallback
+ FallbackFont = text.font;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMeshFont.cs.meta b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMeshFont.cs.meta
new file mode 100644
index 0000000..d8c179f
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/Behaviours/LeanLocalizedTextMeshFont.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 3904cf0d2d9103140ab648dbbee38d3f
+timeCreated: 1561293177
+licenseType: Store
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanLanguage.cs b/Assets/Packages/Lean/Localization/Scripts/LeanLanguage.cs
new file mode 100644
index 0000000..1a39708
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanLanguage.cs
@@ -0,0 +1,43 @@
+using UnityEngine;
+using System.Collections.Generic;
+
+namespace Lean.Localization
+{
+ /// <summary>This class stores information about a single language, and any of its optional cultures.</summary>
+ [System.Serializable]
+ public class LeanLanguage
+ {
+ [SerializeField]
+ private string name;
+
+ [SerializeField]
+ private List<string> cultures;
+
+ public string Name
+ {
+ set
+ {
+ name = value;
+ }
+
+ get
+ {
+ return name;
+ }
+ }
+
+ /// <summary>This culture names for this language (e.g. en-GB, en-US).</summary>
+ public List<string> Cultures
+ {
+ get
+ {
+ if (cultures == null)
+ {
+ cultures = new List<string>();
+ }
+
+ return cultures;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanLanguage.cs.meta b/Assets/Packages/Lean/Localization/Scripts/LeanLanguage.cs.meta
new file mode 100644
index 0000000..31a8774
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanLanguage.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 225ec97158446a94d9cb40a9f742a8b1
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanLanguageCSV.cs b/Assets/Packages/Lean/Localization/Scripts/LeanLanguageCSV.cs
new file mode 100644
index 0000000..f8a89b2
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanLanguageCSV.cs
@@ -0,0 +1,303 @@
+using UnityEngine;
+using System.Collections.Generic;
+using Lean.Common;
+#if UNITY_EDITOR
+using UnityEditor;
+
+namespace Lean.Localization.Examples
+{
+ [CanEditMultipleObjects]
+ [CustomEditor(typeof(LeanLanguageCSV), true)]
+ public class LeanLanguageCSV_Inspector : LeanInspector<LeanLanguageCSV>
+ {
+ protected override void DrawInspector()
+ {
+ Draw("Source", "The text asset that contains all the translations.");
+ Draw("Language", "The language of the translations in the source file.");
+ Draw("Separator", "The string separating the phrase name from the translation.");
+ Draw("NewLine", "The string denoting a new line within a translation.");
+ Draw("Comment", "The (optional) string separating the translation from the comment (empty = no comments).");
+
+ EditorGUILayout.Separator();
+
+ EditorGUI.BeginDisabledGroup(true);
+ EditorGUILayout.LabelField("CollectItem" + Target.Separator + "アイテム" + Target.NewLine + "集めました" + Target.Comment + "Comment here");
+ EditorGUI.EndDisabledGroup();
+
+ EditorGUILayout.Separator();
+
+ EditorGUILayout.BeginHorizontal();
+ if (Any(t => t.Entries.Count > 0))
+ {
+ if (GUILayout.Button("Clear") == true)
+ {
+ Each(t => t.Clear());
+ }
+ }
+ if (GUILayout.Button("Load Now") == true)
+ {
+ Each(t => t.LoadFromSource());
+ }
+ if (GUILayout.Button("Export") == true)
+ {
+ Each(t => t.ExportTextAsset());
+ }
+ EditorGUILayout.EndHorizontal();
+
+ if (Targets.Length == 1)
+ {
+ var entries = Target.Entries;
+
+ if (entries.Count > 0)
+ {
+ EditorGUILayout.Separator();
+
+ EditorGUI.BeginDisabledGroup(true);
+ foreach (var entry in entries)
+ {
+ EditorGUILayout.TextField(entry.Name, entry.Text);
+ }
+ EditorGUI.EndDisabledGroup();
+ }
+ }
+ }
+ }
+}
+#endif
+
+namespace Lean.Localization
+{
+ /// <summary>This component will load localizations from a CSV file. By default they should be in the format:
+ /// Phrase Name Here = Translation Here // Optional Comment Here</summary>
+ [ExecuteInEditMode]
+ [HelpURL(LeanLocalization.HelpUrlPrefix + "LeanLanguageCSV")]
+ [AddComponentMenu(LeanLocalization.ComponentPathPrefix + "Language CSV")]
+ public class LeanLanguageCSV : LeanSource
+ {
+ [System.Serializable]
+ public class Entry
+ {
+ public string Name;
+ public string Text;
+ }
+
+ /// <summary>The text asset that contains all the translations.</summary>
+ public TextAsset Source;
+
+ /// <summary>The language of the translations in the source file.</summary>
+ [LeanLanguageName]
+ public string Language;
+
+ /// <summary>The string separating the phrase name from the translation.</summary>
+ public string Separator = " = ";
+
+ /// <summary>The string denoting a new line within a translation.</summary>
+ public string NewLine = "\\n";
+
+ /// <summary>The (optional) string separating the translation from the comment (empty = no comments).</summary>
+ public string Comment = " // ";
+
+ /// <summary>This stores all currently loaded translations from this CSV file.</summary>
+ public List<Entry> Entries { get { if (entries == null) entries = new List<Entry>(); return entries; } } [SerializeField] private List<Entry> entries;
+
+ /// <summary>The characters used to separate each translation.</summary>
+ private static readonly char[] newlineCharacters = new char[] { '\r', '\n' };
+
+ private static Stack<Entry> entryPool = new Stack<Entry>();
+
+ public override void Compile(string primaryLanguage, string secondaryLanguage)
+ {
+ if (entries == null || entries.Count == 0)
+ {
+ if (Application.isPlaying == true)
+ {
+ LoadFromSource();
+ }
+ }
+
+ if (entries != null)
+ {
+ for (var i = entries.Count - 1; i >= 0; i--)
+ {
+ var entry = entries[i];
+ var translation = LeanLocalization.RegisterTranslation(entry.Name);
+
+ translation.Register(Language, this);
+
+ if (Language == primaryLanguage)
+ {
+ translation.Data = entry.Text;
+ translation.Primary = true;
+ }
+ else if (Language == secondaryLanguage && translation.Primary == false)
+ {
+ translation.Data = entry.Text;
+ }
+ }
+ }
+ }
+
+ [ContextMenu("Clear")]
+ public void Clear()
+ {
+ if (entries != null)
+ {
+ entries.Clear();
+
+ // Update translations?
+ if (LeanLocalization.CurrentLanguage == Language)
+ {
+ LeanLocalization.UpdateTranslations();
+ }
+ }
+ }
+
+ [ContextMenu("Load From Source")]
+ public void LoadFromSource()
+ {
+ if (Source != null && string.IsNullOrEmpty(Language) == false)
+ {
+ for (var i = Entries.Count - 1; i >= 0; i--) // NOTE: Property
+ {
+ entryPool.Push(entries[i]);
+ }
+
+ entries.Clear();
+
+ // Split file into lines, and loop through them all
+ var lines = Source.text.Split(newlineCharacters, System.StringSplitOptions.RemoveEmptyEntries);
+
+ for (var i = 0; i < lines.Length; i++)
+ {
+ var line = lines[i];
+ var equalsIndex = line.IndexOf(Separator);
+
+ // Only consider lines with the Separator character
+ if (equalsIndex != -1)
+ {
+ var name = line.Substring(0, equalsIndex).Trim();
+ var text = line.Substring(equalsIndex + Separator.Length).Trim();
+
+ // Does this entry have a comment?
+ if (string.IsNullOrEmpty(Comment) == false)
+ {
+ var commentIndex = text.LastIndexOf(Comment);
+
+ if (commentIndex != -1)
+ {
+ text = text.Substring(0, commentIndex).Trim();
+ }
+ }
+
+ // Replace newline markers with actual newlines
+ if (string.IsNullOrEmpty(NewLine) == false)
+ {
+ text = text.Replace(NewLine, System.Environment.NewLine);
+ }
+
+ var entry = entryPool.Count > 0 ? entryPool.Pop() : new Entry();
+
+ entry.Name = name;
+ entry.Text = text;
+
+ entries.Add(entry);
+ }
+ }
+
+ // Update translations?
+ if (LeanLocalization.CurrentLanguage == Language)
+ {
+ LeanLocalization.UpdateTranslations();
+ }
+ }
+ }
+
+#if UNITY_EDITOR
+ [ContextMenu("Export Text Asset")]
+ public void ExportTextAsset()
+ {
+ if (string.IsNullOrEmpty(Language) == false)
+ {
+ // Find where we want to save the file
+ var path = EditorUtility.SaveFilePanelInProject("Export Text Asset for " + Language, Language, "txt", "");
+
+ // Make sure we didn't cancel the panel
+ if (string.IsNullOrEmpty(path) == false)
+ {
+ if (LeanLocalization.CurrentLanguage == Language)
+ {
+ DoExportTextAsset(path);
+ }
+ else
+ {
+ var oldLanguage = LeanLocalization.CurrentLanguage;
+
+ LeanLocalization.CurrentLanguage = Language;
+
+ DoExportTextAsset(path);
+
+ LeanLocalization.CurrentLanguage = oldLanguage;
+ }
+ }
+ }
+ }
+
+ private void DoExportTextAsset(string path)
+ {
+ var data = "";
+ var gaps = false;
+
+ // Add all phrase names and existing translations to lines
+ foreach (var pair in LeanLocalization.CurrentTranslations)
+ {
+ var translation = pair.Value;
+
+ if (gaps == true)
+ {
+ data += System.Environment.NewLine;
+ }
+
+ data += pair.Key + Separator;
+ gaps = true;
+
+ if (translation.Data is string)
+ {
+ var text = (string)translation.Data;
+
+ // Replace all new line permutations with the new line token
+ text = text.Replace("\r\n", NewLine);
+ text = text.Replace("\n\r", NewLine);
+ text = text.Replace("\n", NewLine);
+ text = text.Replace("\r", NewLine);
+
+ data += text;
+ }
+ }
+
+ // Write text to file
+ using (var file = System.IO.File.OpenWrite(path))
+ {
+ var encoding = new System.Text.UTF8Encoding();
+ var bytes = encoding.GetBytes(data);
+
+ file.Write(bytes, 0, bytes.Length);
+ }
+
+ // Import asset into project
+ AssetDatabase.ImportAsset(path);
+
+ // Replace Soure with new Text Asset?
+ var textAsset = (TextAsset)AssetDatabase.LoadAssetAtPath(path, typeof(TextAsset));
+
+ if (textAsset != null)
+ {
+ Source = textAsset;
+
+ EditorGUIUtility.PingObject(textAsset);
+
+ EditorUtility.SetDirty(this);
+ }
+ }
+#endif
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanLanguageCSV.cs.meta b/Assets/Packages/Lean/Localization/Scripts/LeanLanguageCSV.cs.meta
new file mode 100644
index 0000000..b472917
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanLanguageCSV.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 086ae75064fca9f46b315403da7f5d20
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanLanguageNameAttribute.cs b/Assets/Packages/Lean/Localization/Scripts/LeanLanguageNameAttribute.cs
new file mode 100644
index 0000000..e613d38
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanLanguageNameAttribute.cs
@@ -0,0 +1,46 @@
+using UnityEngine;
+#if UNITY_EDITOR
+using UnityEditor;
+
+namespace Lean.Localization
+{
+ [CustomPropertyDrawer(typeof(LeanLanguageNameAttribute))]
+ public class LeanLanguageNameDrawer : PropertyDrawer
+ {
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
+ {
+ var rectA = position; rectA.xMax -= 37.0f;
+ var rectB = position; rectB.xMin = rectB.xMax - 35.0f;
+
+ EditorGUI.PropertyField(rectA, property);
+
+ if (GUI.Button(rectB, "List") == true)
+ {
+ var menu = new GenericMenu();
+
+ foreach (var languageName in LeanLocalization.CurrentLanguages.Keys)
+ {
+ menu.AddItem(new GUIContent(languageName), property.stringValue == languageName, () => { property.stringValue = languageName; property.serializedObject.ApplyModifiedProperties(); });
+ }
+
+ if (menu.GetItemCount() > 0)
+ {
+ menu.DropDown(rectB);
+ }
+ else
+ {
+ Debug.LogWarning("Your scene doesn't contain any languages, so the language name list couldn't be created.");
+ }
+ }
+ }
+ }
+}
+#endif
+
+namespace Lean.Localization
+{
+ /// <summary>This attribute allows you to modify a normal string field into one that has a dropdown list that allows you to pick a language.</summary>
+ public class LeanLanguageNameAttribute : PropertyAttribute
+ {
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanLanguageNameAttribute.cs.meta b/Assets/Packages/Lean/Localization/Scripts/LeanLanguageNameAttribute.cs.meta
new file mode 100644
index 0000000..5869dc2
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanLanguageNameAttribute.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d7c5bcbe9b4b15742a9a64d98f3f8305
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanLocalization.cs b/Assets/Packages/Lean/Localization/Scripts/LeanLocalization.cs
new file mode 100644
index 0000000..48c6013
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanLocalization.cs
@@ -0,0 +1,1016 @@
+using UnityEngine;
+using System.Collections.Generic;
+using Lean.Common;
+#if UNITY_EDITOR
+using UnityEditor;
+
+namespace Lean.Localization
+{
+ [CustomEditor(typeof(LeanLocalization))]
+ public class LeanLocalization_Inspector : LeanInspector<LeanLocalization>
+ {
+ static LeanLocalization_Inspector()
+ {
+ AddPresetLanguage("Chinese", "ChineseSimplified", "ChineseTraditional", "zh", "zh-TW", "zh-CN", "zh-HK", "zh-SG", "zh-MO");
+ AddPresetLanguage("English", "en", "en-GB", "en-US", "en-AU", "en-CA", "en-NZ", "en-IE", "en-ZA", "en-JM", "en-en029", "en-BZ", "en-BZ", "en-TT", "en-ZW", "en-PH");
+ AddPresetLanguage("Spanish", "es", "es-ES", "es-MX", "es-GT", "es-CR", "es-PA", "es-DO", "es-VE", "es-CO", "es-PE", "es-AR", "es-EC", "es-CL", "es-UY", "es-PY", "es-BO", "es-SV", "es-SV", "es-HN", "es-NI", "es-PR");
+ AddPresetLanguage("Arabic", "ar", "ar-SA", "ar-IQ", "ar-EG", "ar-LY", "ar-DZ", "ar-MA", "ar-TN", "ar-OM", "ar-YE", "ar-SY", "ar-JO", "ar-LB", "ar-KW", "ar-AE", "ar-BH", "ar-QA");
+ AddPresetLanguage("German", "de", "de-DE", "de-CH", "de-AT", "de-LU", "de-LI");
+ AddPresetLanguage("Korean", "ko", "ko-KR");
+ AddPresetLanguage("French", "fr", "fr-FR", "fr-BE", "fr-CA", "fr-CH", "fr-LU", "fr-MC");
+ AddPresetLanguage("Russian", "ru", "ru-RU");
+ AddPresetLanguage("Japanese", "ja", "ja-JP");
+ AddPresetLanguage("Italian", "it", "it-IT", "it-CH");
+ AddPresetLanguage("Portuguese", "pt", "pt-BR", "pt-PT");
+ AddPresetLanguage("Other...");
+ }
+
+ class PresetLanguage
+ {
+ public string Name;
+ public string[] Cultures;
+ }
+
+ private static List<PresetLanguage> presetLanguages = new List<PresetLanguage>();
+
+ protected override void DrawInspector()
+ {
+ LeanLocalization.UpdateTranslations();
+
+ DrawCurrentLanguage();
+ Draw("SaveLanguage", "Automatically save/load the CurrentLanguage selection to PlayerPrefs? (can be cleared with ClearSave context menu option)");
+
+ EditorGUILayout.Separator();
+
+ Draw("DefaultLanguage", "If the application is started and no language has been loaded or auto detected, this language will be used.");
+ Draw("DetectLanguage", "How should the cultures be used to detect the user's device language?");
+ EditorGUI.BeginDisabledGroup(true);
+ EditorGUI.indentLevel++;
+ switch (Target.DetectLanguage)
+ {
+ case LeanLocalization.DetectType.SystemLanguage:
+ EditorGUILayout.TextField("SystemLanguage", Application.systemLanguage.ToString());
+ break;
+ case LeanLocalization.DetectType.CurrentCulture:
+ EditorGUILayout.TextField("CurrentCulture", System.Globalization.CultureInfo.CurrentCulture.ToString());
+ break;
+ case LeanLocalization.DetectType.CurrentUICulture:
+ EditorGUILayout.TextField("CurrentUICulture", System.Globalization.CultureInfo.CurrentUICulture.ToString());
+ break;
+ }
+ EditorGUI.indentLevel--;
+ EditorGUI.EndDisabledGroup();
+
+ EditorGUILayout.Separator();
+
+ DrawLanguages();
+
+ EditorGUILayout.Separator();
+
+ DrawPrefabs();
+
+ EditorGUILayout.Separator();
+
+ DrawTokens();
+
+ EditorGUILayout.Separator();
+
+ DrawTranslations();
+ }
+
+ private void DrawCurrentLanguage()
+ {
+ var rect = Reserve();
+ var rectA = rect; rectA.xMax -= 37.0f;
+ var rectB = rect; rectB.xMin = rectB.xMax - 35.0f;
+
+ LeanLocalization.CurrentLanguage = EditorGUI.TextField(rectA, "Current Language", LeanLocalization.CurrentLanguage);
+
+ if (GUI.Button(rectB, "List") == true)
+ {
+ var menu = new GenericMenu();
+
+ foreach (var pair in LeanLocalization.CurrentLanguages)
+ {
+ var languageName = pair.Key;
+
+ menu.AddItem(new GUIContent(languageName), LeanLocalization.CurrentLanguage == languageName, () => { LeanLocalization.CurrentLanguage = languageName; });
+ }
+
+ if (menu.GetItemCount() > 0)
+ {
+ menu.DropDown(rectB);
+ }
+ else
+ {
+ Debug.LogWarning("Your scene doesn't contain any languages, so the language name list couldn't be created.");
+ }
+ }
+ }
+
+ private void DrawLanguages()
+ {
+ var languagesProperty = serializedObject.FindProperty("languages");
+
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField("Languages", EditorStyles.boldLabel);
+ if (GUILayout.Button("Add", EditorStyles.miniButton, GUILayout.Width(35.0f)) == true)
+ {
+ var menu = new GenericMenu();
+
+ foreach (var presetLanguage in presetLanguages)
+ {
+ var preset = presetLanguage; menu.AddItem(new GUIContent(presetLanguage.Name), Target.LanguageExists(presetLanguage.Name), () => AddLanguage(preset));
+ }
+
+ menu.ShowAsContext();
+ }
+ EditorGUILayout.EndHorizontal();
+
+ if (languagesProperty.arraySize == 0)
+ {
+ EditorGUILayout.HelpBox("Click the 'Add' button, and select a language.", MessageType.Info);
+ }
+
+ EditorGUI.indentLevel++;
+ for (var i = 0; i < languagesProperty.arraySize; i++)
+ {
+ EditorGUILayout.PropertyField(languagesProperty.GetArrayElementAtIndex(i), true);
+ }
+ EditorGUI.indentLevel--;
+ }
+
+ private void DrawPrefabs()
+ {
+ var rectA = Reserve();
+ var rectB = rectA; rectB.xMin += EditorGUIUtility.labelWidth;
+ EditorGUI.LabelField(rectA, "Prefabs", EditorStyles.boldLabel);
+ var newPrefab = EditorGUI.ObjectField(rectB, "", default(Object), typeof(Object), false);
+ if (newPrefab != null)
+ {
+ Undo.RecordObject(Target, "Add Source");
+
+ Target.AddPrefab(newPrefab);
+
+ Dirty();
+ }
+
+ EditorGUI.indentLevel++;
+ for (var i = 0; i < Target.Prefabs.Count; i++)
+ {
+ DrawPrefabs(i);
+ }
+ EditorGUI.indentLevel--;
+ }
+
+ private int expandPrefab = -1;
+
+ private void DrawPrefabs(int index)
+ {
+ var rectA = Reserve();
+ var rectB = rectA; rectB.xMax -= 22.0f;
+ var rectC = rectA; rectC.xMin = rectC.xMax - 20.0f;
+ var prefab = Target.Prefabs[index];
+ var rebuilt = false;
+ var expand = EditorGUI.Foldout(new Rect(rectA.x, rectA.y, 20, rectA.height), expandPrefab == index, "");
+
+ if (expand == true)
+ {
+ expandPrefab = index;
+ }
+ else if (expandPrefab == index)
+ {
+ expandPrefab = -1;
+ }
+
+ EditorGUI.BeginDisabledGroup(true);
+ BeginError(prefab.Root == null);
+ EditorGUI.ObjectField(rectB, prefab.Root, typeof(Object), false);
+ EndError();
+ if (prefab.Root != null)
+ {
+ Undo.RecordObject(Target, "Rebuild Sources");
+
+ rebuilt |= prefab.RebuildSources();
+
+ if (expand == true)
+ {
+ var sources = prefab.Sources;
+
+ EditorGUI.indentLevel++;
+ foreach (var source in sources)
+ {
+ EditorGUI.ObjectField(Reserve(), source, typeof(LeanSource), false);
+ }
+ EditorGUI.indentLevel--;
+ }
+ }
+ EditorGUI.EndDisabledGroup();
+ if (rebuilt == true)
+ {
+ Dirty();
+ }
+ if (GUI.Button(rectC, "X", EditorStyles.miniButton) == true)
+ {
+ Undo.RecordObject(Target, "Remove Prefab");
+
+ Target.Prefabs.RemoveAt(index);
+
+ Dirty();
+
+ if (expand == true)
+ {
+ expandPrefab = -1;
+ }
+ }
+ }
+
+ private static string translationFilter;
+
+ private LeanTranslation expandTranslation;
+
+ private void DrawTranslations()
+ {
+ var rectA = Reserve();
+ var rectB = rectA; rectB.xMin += EditorGUIUtility.labelWidth; rectB.xMax -= 37.0f;
+ var rectC = rectA; rectC.xMin = rectC.xMax - 35.0f;
+ EditorGUI.LabelField(rectA, "Translations", EditorStyles.boldLabel);
+ translationFilter = EditorGUI.TextField(rectB, "", translationFilter);
+ EditorGUI.BeginDisabledGroup(string.IsNullOrEmpty(translationFilter) == true || LeanLocalization.CurrentTranslations.ContainsKey(translationFilter) == true);
+ if (GUI.Button(rectC, "Add", EditorStyles.miniButton) == true)
+ {
+ var phrase = LeanLocalization.AddPhraseToFirst(translationFilter);
+
+ LeanLocalization.UpdateTranslations();
+
+ Selection.activeObject = phrase;
+
+ EditorGUIUtility.PingObject(phrase);
+ }
+ EditorGUI.EndDisabledGroup();
+
+ if (LeanLocalization.CurrentTranslations.Count == 0 && string.IsNullOrEmpty(translationFilter) == true)
+ {
+ EditorGUILayout.HelpBox("Type in the name of a translation, and click the 'Add' button. Or, drag and drop a prefab that contains some.", MessageType.Info);
+ }
+ else
+ {
+ var total = 0;
+
+ EditorGUI.indentLevel++;
+ foreach (var pair in LeanLocalization.CurrentTranslations)
+ {
+ var name = pair.Key;
+
+ if (string.IsNullOrEmpty(translationFilter) == true || name.IndexOf(translationFilter, System.StringComparison.InvariantCultureIgnoreCase) >= 0)
+ {
+ var translation = pair.Value;
+ var rectT = Reserve();
+ var expand = EditorGUI.Foldout(new Rect(rectT.x, rectT.y, 20, rectT.height), expandTranslation == translation, "");
+
+ if (expand == true)
+ {
+ expandTranslation = translation;
+ }
+ else if (expandTranslation == translation)
+ {
+ expandTranslation = null;
+ }
+
+ CalculateTranslation(pair.Value);
+
+ var data = translation.Data;
+
+ total++;
+
+ EditorGUI.BeginDisabledGroup(true);
+ BeginError(missing.Count > 0 || clashes.Count > 0);
+ if (data is Object)
+ {
+ EditorGUI.ObjectField(rectT, name, (Object)data, typeof(Object), true);
+ }
+ else
+ {
+ EditorGUI.TextField(rectT, name, data != null ? data.ToString() : "");
+ }
+ EndError();
+
+ if (expand == true)
+ {
+ EditorGUI.indentLevel++;
+ foreach (var entry in translation.Entries)
+ {
+ BeginError(clashes.Contains(entry.Language) == true);
+ EditorGUILayout.ObjectField(entry.Language, entry.Owner, typeof(Object), true);
+ EndError();
+ }
+ EditorGUI.indentLevel--;
+ }
+ EditorGUI.EndDisabledGroup();
+
+ if (expand == true)
+ {
+ foreach (var language in missing)
+ {
+ EditorGUILayout.HelpBox("This translation isn't defined for the " + language + " language.", MessageType.Warning);
+ }
+
+ foreach (var language in clashes)
+ {
+ EditorGUILayout.HelpBox("This translation is defined multiple times for the " + language + " language.", MessageType.Warning);
+ }
+ }
+ }
+ }
+ EditorGUI.indentLevel--;
+
+ if (total == 0)
+ {
+ EditorGUILayout.HelpBox("No translation with this name exists, click the 'Add' button to create it.", MessageType.Info);
+ }
+ }
+ }
+
+ private static List<string> missing = new List<string>();
+
+ private static List<string> clashes = new List<string>();
+
+ private static void CalculateTranslation(LeanTranslation translation)
+ {
+ missing.Clear();
+ clashes.Clear();
+
+ foreach (var language in LeanLocalization.CurrentLanguages.Keys)
+ {
+ if (translation.Entries.Exists(e => e.Language == language) == false)
+ {
+ missing.Add(language);
+ }
+ }
+
+ foreach (var entry in translation.Entries)
+ {
+ var language = entry.Language;
+
+ if (clashes.Contains(language) == false)
+ {
+ if (translation.LanguageCount(language) > 1)
+ {
+ clashes.Add(language);
+ }
+ }
+ }
+ }
+
+ private static string tokensFilter;
+
+ private void DrawTokens()
+ {
+ var rectA = Reserve();
+ var rectB = rectA; rectB.xMin += EditorGUIUtility.labelWidth; rectB.xMax -= 37.0f;
+ var rectC = rectA; rectC.xMin = rectC.xMax - 35.0f;
+ EditorGUI.LabelField(rectA, "Tokens", EditorStyles.boldLabel);
+ tokensFilter = EditorGUI.TextField(rectB, "", tokensFilter);
+ EditorGUI.BeginDisabledGroup(string.IsNullOrEmpty(tokensFilter) == true || LeanLocalization.CurrentTokens.ContainsKey(tokensFilter) == true);
+ if (GUI.Button(rectC, "Add", EditorStyles.miniButton) == true)
+ {
+ var token = LeanLocalization.AddTokenToFirst(tokensFilter);
+
+ LeanLocalization.UpdateTranslations();
+
+ Selection.activeObject = token;
+
+ EditorGUIUtility.PingObject(token);
+ }
+ EditorGUI.EndDisabledGroup();
+
+ if (LeanLocalization.CurrentTokens.Count > 0 || string.IsNullOrEmpty(tokensFilter) == false)
+ {
+ var total = 0;
+
+ EditorGUI.indentLevel++;
+ EditorGUI.BeginDisabledGroup(true);
+ foreach (var pair in LeanLocalization.CurrentTokens)
+ {
+ if (string.IsNullOrEmpty(tokensFilter) == true || pair.Key.IndexOf(tokensFilter, System.StringComparison.InvariantCultureIgnoreCase) >= 0)
+ {
+ EditorGUILayout.ObjectField(pair.Key, pair.Value, typeof(Object), true); total++;
+ }
+ }
+ EditorGUI.EndDisabledGroup();
+ EditorGUI.indentLevel--;
+
+ if (total == 0)
+ {
+ EditorGUILayout.HelpBox("No token with this name exists, click the 'Add' button to create it.", MessageType.Info);
+ }
+ }
+ }
+
+ private void AddLanguage(PresetLanguage presetLanguage)
+ {
+ Undo.RecordObject(Target, "Add Language");
+
+ Target.AddLanguage(presetLanguage.Name, presetLanguage.Cultures);
+
+ Dirty();
+ }
+
+ private static void AddPresetLanguage(string name, params string[] cultures)
+ {
+ var presetLanguage = new PresetLanguage();
+
+ presetLanguage.Name = name;
+ presetLanguage.Cultures = cultures;
+
+ presetLanguages.Add(presetLanguage);
+ }
+
+ [MenuItem("GameObject/Lean/Localization", false, 1)]
+ private static void CreateLocalization()
+ {
+ var gameObject = new GameObject(typeof(LeanLocalization).Name);
+
+ Undo.RegisterCreatedObjectUndo(gameObject, "Create LeanLocalization");
+
+ gameObject.AddComponent<LeanLocalization>();
+
+ Selection.activeGameObject = gameObject;
+ }
+ }
+}
+#endif
+
+namespace Lean.Localization
+{
+ /// <summary>This component manages a global list of translations for easy access.
+ /// Translations are gathered from the <b>prefabs</b> list, as well as from any active and enabled <b>LeanSource</b> components in the scene.</summary>
+ [ExecuteInEditMode]
+ [HelpURL(HelpUrlPrefix + "LeanLocalization")]
+ [AddComponentMenu(ComponentPathPrefix + "Localization")]
+ public class LeanLocalization : MonoBehaviour
+ {
+ public enum DetectType
+ {
+ None,
+ SystemLanguage,
+ CurrentCulture,
+ CurrentUICulture
+ }
+
+ public const string HelpUrlPrefix = LeanHelper.HelpUrlPrefix + "LeanLocalization#";
+
+ public const string ComponentPathPrefix = LeanHelper.ComponentPathPrefix + "Localization/Lean ";
+
+ /// <summary>All active and enabled LeanLocalization components.</summary>
+ public static List<LeanLocalization> Instances = new List<LeanLocalization>();
+
+ public static Dictionary<string, LeanToken> CurrentTokens = new Dictionary<string, LeanToken>();
+
+ public static Dictionary<string, LeanLanguage> CurrentLanguages = new Dictionary<string, LeanLanguage>();
+
+ /// <summary>Dictionary of all the phrase names mapped to their current translations.</summary>
+ public static Dictionary<string, LeanTranslation> CurrentTranslations = new Dictionary<string, LeanTranslation>();
+
+ /// <summary>If the application is started and no language has been loaded or auto detected, this language will be used.</summary>
+ [LeanLanguageName]
+ public string DefaultLanguage;
+
+ /// <summary>How should the cultures be used to detect the user's device language?</summary>
+ public DetectType DetectLanguage = DetectType.SystemLanguage;
+
+ /// <summary>Automatically save/load the CurrentLanguage selection to PlayerPrefs? (can be cleared with ClearSave context menu option)</summary>
+ public bool SaveLanguage = true;
+
+ [SerializeField]
+ private List<LeanLanguage> languages;
+
+ [SerializeField]
+ private List<LeanPrefab> prefabs;
+
+ /// <summary>Called when the language or translations change.</summary>
+ public static System.Action OnLocalizationChanged;
+
+ /// <summary>The currently set language.</summary>
+ private static string currentLanguage;
+
+ private static bool pendingUpdates;
+
+ private static Dictionary<string, LeanTranslation> tempTranslations = new Dictionary<string, LeanTranslation>();
+
+ /// <summary>This stores all languages and their aliases managed by this LeanLocalization instance.</summary>
+ public List<LeanLanguage> Languages
+ {
+ get
+ {
+ if (languages == null)
+ {
+ languages = new List<LeanLanguage>();
+ }
+
+ return languages;
+ }
+ }
+
+ /// <summary>This stores all prefabs and folders managed by this LeanLocalization instance.</summary>
+ public List<LeanPrefab> Prefabs
+ {
+ get
+ {
+ if (prefabs == null)
+ {
+ prefabs = new List<LeanPrefab>();
+ }
+
+ return prefabs;
+ }
+ }
+
+ /// <summary>Does at least one localization have 'SaveLanguage' set?</summary>
+ public static bool CurrentSaveLanguage
+ {
+ get
+ {
+ for (var i = 0; i < Instances.Count; i++)
+ {
+ if (Instances[i].SaveLanguage == true)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ /// <summary>Change the current language of this instance?</summary>
+ public static string CurrentLanguage
+ {
+ set
+ {
+ if (CurrentLanguage != value)
+ {
+ currentLanguage = value;
+
+ UpdateTranslations();
+
+ if (CurrentSaveLanguage == true)
+ {
+ PlayerPrefs.SetString("LeanLocalization.CurrentLanguage", value);
+ }
+ }
+ }
+
+ get
+ {
+ return currentLanguage;
+ }
+ }
+
+ /// <summary>When rebuilding translations this method is called from any <b>LeanSource</b> components that define a transition.</summary>
+ public static LeanTranslation RegisterTranslation(string name)
+ {
+ var translation = default(LeanTranslation);
+
+ if (string.IsNullOrEmpty(name) == false && CurrentTranslations.TryGetValue(name, out translation) == false)
+ {
+ if (tempTranslations.TryGetValue(name, out translation) == true)
+ {
+ tempTranslations.Remove(name);
+
+ CurrentTranslations.Add(name, translation);
+ }
+ else
+ {
+ translation = new LeanTranslation(name);
+
+ CurrentTranslations.Add(name, translation);
+ }
+ }
+
+ return translation;
+ }
+
+ [ContextMenu("Clear Save")]
+ public void ClearSave()
+ {
+ PlayerPrefs.DeleteKey("LeanLocalization.CurrentLanguage");
+ }
+
+ /// <summary>This sets the current language using the specified string.</summary>
+ public void SetCurrentLanguage(string newLanguage)
+ {
+ CurrentLanguage = newLanguage;
+ }
+
+ /// <summary>This sets the current language using the specified index based on the Languages list, where 0 is the first language.</summary>
+ public void SetCurrentLanguage(int newLanguageIndex)
+ {
+ if (newLanguageIndex >= 0 && newLanguageIndex < Instances.Count)
+ {
+ SetCurrentLanguage(Instances[newLanguageIndex].name);
+ }
+ }
+
+ public bool LanguageExists(string languageName)
+ {
+ var language = default(LeanLanguage);
+
+ return TryGetLanguage(languageName, ref language);
+ }
+
+ public bool TryGetLanguage(string languageName, ref LeanLanguage language)
+ {
+ if (languages != null)
+ {
+ for (var i = languages.Count - 1; i >= 0; i--)
+ {
+ language = languages[i];
+
+ if (language.Name == languageName)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /// <summary>This adds the specified UnityEngine.Object to this LeanLocalization instance, allowing it to be registered as a prefab.</summary>
+ public void AddPrefab(Object root)
+ {
+ for (var i = Prefabs.Count - 1; i >= 0; i--) // NOTE: Property
+ {
+ if (prefabs[i].Root == root)
+ {
+ return;
+ }
+ }
+
+ var prefab = new LeanPrefab();
+
+ prefab.Root = root;
+
+ prefabs.Add(prefab);
+ }
+
+ /// <summary>This adds a new language to this LeanLocalization instance, with the specified name and cultures.</summary>
+ public LeanLanguage AddLanguage(string languageName, string[] cultures)
+ {
+ var language = default(LeanLanguage);
+
+ if (TryGetLanguage(languageName, ref language) == false)
+ {
+ language = new LeanLanguage();
+
+ language.Name = languageName;
+
+ if (languages == null)
+ {
+ languages = new List<LeanLanguage>();
+ }
+
+ languages.Add(language);
+ }
+
+ language.Cultures.Clear();
+ language.Cultures.AddRange(cultures);
+
+ return language;
+ }
+
+ /// <summary>This calls AddToken on the first active and enabled LeanLocalization instance, or creates one first.</summary>
+ public static LeanToken AddTokenToFirst(string name)
+ {
+ if (Instances.Count == 0)
+ {
+ new GameObject("LeanLocalization").AddComponent<LeanLocalization>();
+ }
+
+ return Instances[0].AddToken(name);
+ }
+
+ /// <summary>This creates a new token with the specified name, and adds it to the current GameObject.</summary>
+ public LeanToken AddToken(string name)
+ {
+ if (string.IsNullOrEmpty(name) == false)
+ {
+ var root = new GameObject(name);
+ var token = root.AddComponent<LeanToken>();
+
+ root.transform.SetParent(transform, false);
+
+ return token;
+ }
+
+ return null;
+ }
+
+ /// <summary>This allows you to set the value of the token with the specified name.
+ /// If no token exists and allowCreation is enabled, then one will be created for you.</summary>
+ public static void SetToken(string name, string value, bool allowCreation = true)
+ {
+ if (string.IsNullOrEmpty(name) == false)
+ {
+ var token = default(LeanToken);
+
+ if (CurrentTokens.TryGetValue(name, out token) == true)
+ {
+ token.Value = value;
+ }
+ else if (allowCreation == true)
+ {
+ token = AddTokenToFirst(name);
+
+ token.Value = value;
+ }
+ }
+ }
+
+ /// <summary>This allows you to get the value of the token with the specified name.
+ /// If no token exists, then the defaultValue will be returned.</summary>
+ public static string GetToken(string name, string defaultValue = null)
+ {
+ var token = default(LeanToken);
+
+ if (string.IsNullOrEmpty(name) == false)
+ {
+ if (CurrentTokens.TryGetValue(name, out token) == true)
+ {
+ return token.Value;
+ }
+ }
+
+ return defaultValue;
+ }
+
+ /// <summary>This calls AddPhrase on the first active and enabled LeanLocalization instance, or creates one first.</summary>
+ public static LeanPhrase AddPhraseToFirst(string name)
+ {
+ if (Instances.Count == 0)
+ {
+ new GameObject("LeanLocalization").AddComponent<LeanLocalization>();
+ }
+
+ return Instances[0].AddPhrase(name);
+ }
+
+ /// <summary>This creates a new phrase with the specified name, and adds it to the current GameObject.</summary>
+ public LeanPhrase AddPhrase(string name)
+ {
+ if (string.IsNullOrEmpty(name) == false)
+ {
+ var root = new GameObject(name);
+ var phrase = root.AddComponent<LeanPhrase>();
+
+ root.transform.SetParent(transform, false);
+
+ return phrase;
+ }
+
+ return null;
+ }
+
+ /// <summary>This will return the translation with the specified name, or null if none was found.</summary>
+ public static LeanTranslation GetTranslation(string name)
+ {
+ var translation = default(LeanTranslation);
+
+ if (string.IsNullOrEmpty(name) == false)
+ {
+ CurrentTranslations.TryGetValue(name, out translation);
+ }
+
+ return translation;
+ }
+
+ /// <summary>This will return the translated string with the specified name, or the fallback if none is found.</summary>
+ public static string GetTranslationText(string name, string fallback = null)
+ {
+ var translation = default(LeanTranslation);
+
+ if (string.IsNullOrEmpty(name) == false && CurrentTranslations.TryGetValue(name, out translation) == true && translation.Data is string)
+ {
+ return (string)translation.Data;
+ }
+
+ return fallback;
+ }
+
+ /// <summary>This will return the translated UnityEngine.Object with the specified name, or the fallback if none is found.</summary>
+ public static T GetTranslationObject<T>(string name, T fallback = null)
+ where T : Object
+ {
+ var translation = default(LeanTranslation);
+
+ if (string.IsNullOrEmpty(name) == false && CurrentTranslations.TryGetValue(name, out translation) == true && translation.Data is T)
+ {
+ return (T)translation.Data;
+ }
+
+ return fallback;
+ }
+
+ /// <summary>This rebuilds the dictionary used to quickly map phrase names to translations for the current language.</summary>
+ public static void UpdateTranslations()
+ {
+ pendingUpdates = false;
+
+ // Copy previous translations to temp dictionary
+ tempTranslations.Clear();
+
+ foreach (var pair in CurrentTranslations)
+ {
+ var translation = pair.Value;
+
+ translation.Clear();
+
+ tempTranslations.Add(pair.Key, translation);
+ }
+
+ // Clear currents
+ CurrentTokens.Clear();
+ CurrentLanguages.Clear();
+ CurrentTranslations.Clear();
+
+ // Rebuild all currents
+ for (var i = 0; i < Instances.Count; i++)
+ {
+ Instances[i].RegisterAndBuild();
+ }
+
+ // Notify changes?
+ if (OnLocalizationChanged != null)
+ {
+ OnLocalizationChanged();
+ }
+ }
+
+ /// <summary>If you call this method, then UpdateTranslations will be called next Update.</summary>
+ public static void DelayUpdateTranslations()
+ {
+ pendingUpdates = true;
+
+#if UNITY_EDITOR
+ // Go through all enabled phrases
+ for (var i = 0; i < Instances.Count; i++)
+ {
+ EditorUtility.SetDirty(Instances[i].gameObject);
+ }
+#endif
+ }
+
+ /// <summary>Set the instance, merge old instance, and update translations.</summary>
+ protected virtual void OnEnable()
+ {
+ Instances.Add(this);
+
+ UpdateCurrentLanguage();
+
+ UpdateTranslations();
+ }
+
+ /// <summary>Unset instance?</summary>
+ protected virtual void OnDisable()
+ {
+ Instances.Remove(this);
+
+ UpdateTranslations();
+ }
+
+ protected virtual void Update()
+ {
+ if (pendingUpdates == true)
+ {
+ UpdateTranslations();
+ }
+ }
+#if UNITY_EDITOR
+ // Inspector modified?
+ protected virtual void OnValidate()
+ {
+ UpdateTranslations();
+ }
+#endif
+ private void RegisterAndBuild()
+ {
+ if (languages != null)
+ {
+ for (var i = 0; i < languages.Count; i++)
+ {
+ var language = languages[i];
+
+ if (language != null && string.IsNullOrEmpty(language.Name) == false)
+ {
+ if (CurrentLanguages.ContainsKey(language.Name) == false)
+ {
+ CurrentLanguages.Add(language.Name, language);
+ }
+ }
+ }
+ }
+
+ if (prefabs != null)
+ {
+ for (var i = 0; i < prefabs.Count; i++)
+ {
+ var sources = prefabs[i].Sources;
+
+ for (var j = 0; j < sources.Count; j++)
+ {
+ sources[j].Compile(currentLanguage, DefaultLanguage);
+ }
+ }
+ }
+
+ var source = LeanSource.Instances.First;
+
+ for (var i = LeanSource.Instances.Count - 1; i >= 0; i--)
+ {
+ source.Value.Compile(currentLanguage, DefaultLanguage);
+
+ source = source.Next;
+ }
+ }
+
+ private void UpdateCurrentLanguage()
+ {
+ // Load saved language?
+ if (string.IsNullOrEmpty(currentLanguage) == true)
+ {
+ if (SaveLanguage == true)
+ {
+ currentLanguage = PlayerPrefs.GetString("LeanLocalization.CurrentLanguage");
+ }
+ }
+
+ // Find language by culture?
+ if (string.IsNullOrEmpty(currentLanguage) == true)
+ {
+ switch (DetectLanguage)
+ {
+ case DetectType.SystemLanguage:
+ {
+ currentLanguage = FindLanguageName(Application.systemLanguage.ToString());
+ }
+ break;
+
+ case DetectType.CurrentCulture:
+ {
+ var cultureInfo = System.Globalization.CultureInfo.CurrentCulture;
+
+ if (cultureInfo != null)
+ {
+ currentLanguage = FindLanguageName(cultureInfo.Name);
+ }
+ }
+ break;
+
+ case DetectType.CurrentUICulture:
+ {
+ var cultureInfo = System.Globalization.CultureInfo.CurrentUICulture;
+
+ if (cultureInfo != null)
+ {
+ currentLanguage = FindLanguageName(cultureInfo.Name);
+ }
+ }
+ break;
+ }
+ }
+
+ // Use default language?
+ if (string.IsNullOrEmpty(currentLanguage) == true)
+ {
+ currentLanguage = DefaultLanguage;
+ }
+ }
+
+ private string FindLanguageName(string alias)
+ {
+ for (var i = Languages.Count - 1; i >= 0; i--)
+ {
+ var language = Languages[i];
+
+ if (language.Name == alias)
+ {
+ return language.Name;
+ }
+
+ if (language.Cultures != null)
+ {
+ for (var j = language.Cultures.Count - 1; j >= 0; j--)
+ {
+ if (language.Cultures[j] == alias)
+ {
+ return language.Name;
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanLocalization.cs.meta b/Assets/Packages/Lean/Localization/Scripts/LeanLocalization.cs.meta
new file mode 100644
index 0000000..77c6c41
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanLocalization.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 806f0696300aeac44a0e17efee222854
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanLocalizedBehaviour.cs b/Assets/Packages/Lean/Localization/Scripts/LeanLocalizedBehaviour.cs
new file mode 100644
index 0000000..4163464
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanLocalizedBehaviour.cs
@@ -0,0 +1,121 @@
+using UnityEngine;
+using UnityEngine.Serialization;
+using System.Collections.Generic;
+using Lean.Common;
+#if UNITY_EDITOR
+using UnityEditor;
+
+namespace Lean.Localization
+{
+ [CanEditMultipleObjects]
+ [CustomEditor(typeof(LeanLocalizedBehaviour), true)]
+ public class LeanLocalizedBehaviour_Inspector : LeanInspector<LeanLocalizedBehaviour>
+ {
+ }
+}
+#endif
+
+namespace Lean.Localization
+{
+ /// <summary>This component simplifies the updating process, extend it if you want to cause a specific object to get localized</summary>
+ public abstract class LeanLocalizedBehaviour : MonoBehaviour
+ {
+ [Tooltip("The name of the phrase we want to use for this localized component")]
+ [SerializeField]
+ [LeanTranslationName]
+ [FormerlySerializedAs("phraseName")]
+ [FormerlySerializedAs("translationTitle")]
+ private string translationName;
+
+ [System.NonSerialized]
+ private HashSet<LeanToken> tokens;
+
+ /// <summary>This is the name of the translation this script uses.</summary>
+ public string TranslationName
+ {
+ set
+ {
+ if (translationName != value)
+ {
+ translationName = value;
+
+ UpdateLocalization();
+ }
+ }
+
+ get
+ {
+ return translationName;
+ }
+ }
+
+ public void Register(LeanToken token)
+ {
+ if (token != null)
+ {
+ if (tokens == null)
+ {
+ tokens = new HashSet<LeanToken>();
+ }
+
+ tokens.Add(token);
+ }
+ }
+
+ public void Unregister(LeanToken token)
+ {
+ if (tokens != null)
+ {
+ tokens.Remove(token);
+ }
+ }
+
+ public void UnregisterAll()
+ {
+ if (tokens != null)
+ {
+ foreach (var token in tokens)
+ {
+ token.Unregister(this);
+ }
+
+ tokens.Clear();
+ }
+ }
+
+ // This gets called every time the translation needs updating
+ // NOTE: translation may be null if it can't be found
+ public abstract void UpdateTranslation(LeanTranslation translation);
+
+ /// <summary>If you call this then this component will update using the translation for the specified phrase.</summary>
+ [ContextMenu("Update Localization")]
+ public void UpdateLocalization()
+ {
+ UpdateTranslation(LeanLocalization.GetTranslation(translationName));
+ }
+
+ protected virtual void OnEnable()
+ {
+ LeanLocalization.OnLocalizationChanged += UpdateLocalization;
+
+ UpdateLocalization();
+ }
+
+ protected virtual void OnDisable()
+ {
+ LeanLocalization.OnLocalizationChanged -= UpdateLocalization;
+
+ UnregisterAll();
+ }
+
+#if UNITY_EDITOR
+ protected virtual void OnValidate()
+ {
+ if (isActiveAndEnabled == true)
+ {
+ UpdateLocalization();
+ }
+ }
+#endif
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanLocalizedBehaviour.cs.meta b/Assets/Packages/Lean/Localization/Scripts/LeanLocalizedBehaviour.cs.meta
new file mode 100644
index 0000000..970eeae
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanLocalizedBehaviour.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 1fbd7e2a5aacea442bccddc4c9c84fa9
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanPhrase.cs b/Assets/Packages/Lean/Localization/Scripts/LeanPhrase.cs
new file mode 100644
index 0000000..9c329e4
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanPhrase.cs
@@ -0,0 +1,299 @@
+using UnityEngine;
+using System.Collections.Generic;
+using Lean.Common;
+#if UNITY_EDITOR
+using UnityEditor;
+
+namespace Lean.Localization
+{
+ [CustomEditor(typeof(LeanPhrase))]
+ public class LeanPhrase_Inspector : LeanInspector<LeanPhrase>
+ {
+ private static List<string> languageNames = new List<string>();
+
+ private static List<LeanPhrase.Entry> entries = new List<LeanPhrase.Entry>();
+
+ protected override void DrawInspector()
+ {
+ entries.Clear();
+ entries.AddRange(Target.Entries);
+
+ languageNames.Clear();
+ languageNames.AddRange(LeanLocalization.CurrentLanguages.Keys);
+
+ Target.Data = (LeanPhrase.DataType)GUILayout.Toolbar((int)Target.Data, new string[] { "Text", "Object", "Sprite" });
+
+ EditorGUILayout.Separator();
+
+ foreach (var languageName in languageNames)
+ {
+ var entry = default(LeanPhrase.Entry);
+
+ if (Target.TryFindTranslation(languageName, ref entry) == true)
+ {
+ DrawEntry(entry, false);
+
+ entries.Remove(entry);
+ }
+ else
+ {
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField(languageName, EditorStyles.boldLabel);
+ if (GUILayout.Button("Create", EditorStyles.miniButton, GUILayout.Width(45.0f)) == true)
+ {
+ Undo.RecordObject(Target, "Create Translation");
+
+ Target.AddEntry(languageName);
+
+ Dirty();
+ }
+ EditorGUILayout.EndHorizontal();
+ }
+
+ EditorGUILayout.Separator();
+ }
+
+ if (entries.Count > 0)
+ {
+ foreach (var entry in entries)
+ {
+ DrawEntry(entry, true);
+ }
+ }
+ }
+
+ private void DrawEntry(LeanPhrase.Entry entry, bool unexpected)
+ {
+ EditorGUILayout.BeginHorizontal();
+ EditorGUILayout.LabelField(entry.Language, EditorStyles.boldLabel);
+ if (GUILayout.Button("Remove", EditorStyles.miniButton, GUILayout.Width(55.0f)) == true)
+ {
+ Undo.RecordObject(Target, "Remove Translation");
+
+ Target.RemoveTranslation(entry.Language);
+
+ Dirty();
+ }
+ EditorGUILayout.EndHorizontal();
+
+ if (unexpected == true)
+ {
+ EditorGUILayout.HelpBox("Your LeanLocalization component doesn't define the " + entry.Language + " language.", MessageType.Warning);
+ }
+
+ Undo.RecordObject(Target, "Modified Translation");
+
+ EditorGUI.BeginChangeCheck();
+
+ switch (Target.Data)
+ {
+ case LeanPhrase.DataType.Text:
+ entry.Text = EditorGUILayout.TextArea(entry.Text ?? "", GUILayout.MinHeight(40.0f));
+ break;
+ case LeanPhrase.DataType.Object:
+ entry.Object = EditorGUILayout.ObjectField(entry.Object, typeof(Object), true);
+ break;
+ case LeanPhrase.DataType.Sprite:
+ entry.Object = EditorGUILayout.ObjectField(entry.Object, typeof(Sprite), true);
+ break;
+ }
+
+ if (EditorGUI.EndChangeCheck() == true)
+ {
+ Dirty(); LeanLocalization.UpdateTranslations();
+ }
+
+ EditorGUILayout.Separator();
+ }
+
+ [MenuItem("Assets/Create/Lean/Localization/Lean Phrase")]
+ private static void CreatePhrase()
+ {
+ var instance = new GameObject("New Phrase").AddComponent<LeanPhrase>();
+ var path = AssetDatabase.GetAssetPath(Selection.activeObject);
+
+ if (string.IsNullOrEmpty(path) == true)
+ {
+ path = "Assets";
+ }
+
+ path = AssetDatabase.GenerateUniqueAssetPath(path + "/New Phrase.asset");
+
+ AssetDatabase.CreateAsset(instance, path);
+
+ Selection.activeObject = instance;
+ }
+ }
+}
+#endif
+
+namespace Lean.Localization
+{
+ /// <summary>This contains data about each phrase, which is then translated into different languages.</summary>
+ [ExecuteInEditMode]
+ [DisallowMultipleComponent]
+ [HelpURL(LeanLocalization.HelpUrlPrefix + "LeanPhrase")]
+ [AddComponentMenu(LeanLocalization.ComponentPathPrefix + "Phrase")]
+ public class LeanPhrase : LeanSource
+ {
+ public enum DataType
+ {
+ Text,
+ Object,
+ Sprite
+ }
+
+ [System.Serializable]
+ public class Entry
+ {
+ /// <summary>The language of this translation.</summary>
+ public string Language;
+
+ /// <summary>The translated text.</summary>
+ public string Text;
+
+ /// <summary>The translated object (e.g. language specific texture).</summary>
+ public Object Object;
+ }
+
+ public DataType Data { set { data = value; } get { return data; } } [SerializeField] private DataType data;
+
+ /// <summary>This list stores all translations of this phrase in each language.</summary>
+ [SerializeField]
+ [UnityEngine.Serialization.FormerlySerializedAs("translations")]
+ private List<Entry> entries;
+
+ public List<Entry> Entries
+ {
+ get
+ {
+ if (entries == null)
+ {
+ entries = new List<Entry>();
+ }
+
+ return entries;
+ }
+ }
+
+ public void Clear()
+ {
+ if (entries != null)
+ {
+ entries.Clear();
+ }
+ }
+
+ public override void Compile(string primaryLanguage, string secondaryLanguage)
+ {
+ var translation = LeanLocalization.RegisterTranslation(name);
+
+ if (entries != null)
+ {
+ for (var i = entries.Count - 1; i >= 0; i--)
+ {
+ var entry = entries[i];
+
+ translation.Register(entry.Language, this);
+
+ if (entry.Language == primaryLanguage)
+ {
+ Compile(translation, entry, true);
+ }
+ else if (entry.Language == secondaryLanguage && translation.Primary == false)
+ {
+ Compile(translation, entry, false);
+ }
+ }
+ }
+ }
+
+ private void Compile(LeanTranslation translation, Entry entry, bool primary)
+ {
+ switch (data)
+ {
+ case DataType.Text:
+ {
+ Compile(translation, entry.Text, primary);
+ }
+ break;
+ case DataType.Object:
+ case DataType.Sprite:
+ {
+ Compile(translation, entry.Object, primary);
+ }
+ break;
+ }
+ }
+
+ private void Compile(LeanTranslation translation, object data, bool primary)
+ {
+ translation.Data = data;
+
+ if (primary == true)
+ {
+ translation.Primary = true;
+ }
+ }
+
+ /// <summary>This will return the translation of this phrase for the specified language.</summary>
+ public bool TryFindTranslation(string languageName, ref Entry entry)
+ {
+ if (entries != null)
+ {
+ for (var i = entries.Count - 1; i >= 0; i--)
+ {
+ entry = entries[i];
+
+ if (entry.Language == languageName)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public void RemoveTranslation(string languageName)
+ {
+ if (entries != null)
+ {
+ for (var i = entries.Count - 1; i >= 0; i--)
+ {
+ if (entries[i].Language == languageName)
+ {
+ entries.RemoveAt(i);
+
+ return;
+ }
+ }
+ }
+ }
+
+ /// <summary>Add a new translation to this phrase for the specified language, or return the current one.</summary>
+ public Entry AddEntry(string languageName, string text = null, Object obj = null)
+ {
+ var translation = default(Entry);
+
+ if (TryFindTranslation(languageName, ref translation) == false)
+ {
+ translation = new Entry();
+
+ translation.Language = languageName;
+
+ if (entries == null)
+ {
+ entries = new List<Entry>();
+ }
+
+ entries.Add(translation);
+ }
+
+ translation.Text = text;
+ translation.Object = obj;
+
+ return translation;
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanPhrase.cs.meta b/Assets/Packages/Lean/Localization/Scripts/LeanPhrase.cs.meta
new file mode 100644
index 0000000..13d65e0
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanPhrase.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: eb15fa0e7301ff849bf4d6dcede7c12b
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanPrefab.cs b/Assets/Packages/Lean/Localization/Scripts/LeanPrefab.cs
new file mode 100644
index 0000000..1507767
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanPrefab.cs
@@ -0,0 +1,176 @@
+using UnityEngine;
+using System.IO;
+using System.Collections.Generic;
+
+namespace Lean.Localization
+{
+ [System.Serializable]
+ public class LeanPrefab
+ {
+ public Object Root;
+
+ [SerializeField]
+ private List<LeanSource> sources;
+
+ [System.NonSerialized]
+ private int buildingCount;
+
+ [System.NonSerialized]
+ private bool buildingModified;
+
+ private static List<LeanSource> tempSources = new List<LeanSource>();
+
+ public List<LeanSource> Sources
+ {
+ get
+ {
+ if (sources == null)
+ {
+ sources = new List<LeanSource>();
+ }
+
+ return sources;
+ }
+ }
+
+ public bool RebuildSources()
+ {
+ if (sources == null)
+ {
+ sources = new List<LeanSource>();
+ }
+
+ if (Root != null)
+ {
+ if (Root is LeanSource)
+ {
+ buildingCount = 0;
+ buildingModified = false;
+
+ AddSource((LeanSource)Root);
+
+ return FinalizeBuild();
+ }
+ else if (Root is GameObject)
+ {
+ buildingCount = 0;
+ buildingModified = false;
+
+ FindFromGameObject(((GameObject)Root).transform);
+
+ return FinalizeBuild();
+ }
+#if UNITY_EDITOR
+ else // Folder
+ {
+ var rootPath = UnityEditor.AssetDatabase.GetAssetPath(Root);
+
+ if (string.IsNullOrEmpty(rootPath) == false)
+ {
+ buildingCount = 0;
+ buildingModified = false;
+
+ var basePath = Application.dataPath;
+ var baseTail = "Assets";
+
+ if (basePath.EndsWith(baseTail) == true)
+ {
+ basePath = basePath.Substring(0, basePath.Length - baseTail.Length);
+ }
+
+ FindFromFolder(basePath, rootPath);
+
+ return FinalizeBuild();
+ }
+ }
+#endif
+ }
+
+ return false;
+ }
+
+ private bool FinalizeBuild()
+ {
+ for (var i = sources.Count - 1; i >= buildingCount; i--)
+ {
+ sources.RemoveAt(i); buildingModified = true;
+ }
+
+ return buildingModified;
+ }
+
+ private void AddSource(LeanSource source)
+ {
+ if (buildingCount < sources.Count)
+ {
+ if (sources[buildingCount] != source)
+ {
+ sources[buildingCount] = source;
+
+ buildingModified = true;
+ }
+ }
+ else
+ {
+ sources.Add(source);
+
+ buildingModified = true;
+ }
+
+ buildingCount++;
+ }
+
+ private void FindFromGameObject(Transform prefab)
+ {
+ prefab.GetComponents(tempSources);
+
+ if (tempSources.Count > 0)
+ {
+ for (var i = 0; i < tempSources.Count; i++)
+ {
+ AddSource(tempSources[i]);
+ }
+ }
+ else
+ {
+ for (var i = 0; i < prefab.childCount; i++)
+ {
+ FindFromGameObject(prefab.GetChild(i));
+ }
+ }
+ }
+#if UNITY_EDITOR
+ private void FindFromFolder(string basePath, string rootPath)
+ {
+ var fullPath = basePath + rootPath;
+
+ if (Directory.Exists(fullPath) == true)
+ {
+ var subFolders = Directory.GetDirectories(fullPath);
+
+ for (var i = 0; i < subFolders.Length; i++)
+ {
+ FindFromFolder(basePath, subFolders[i].Substring(basePath.Length));
+ }
+
+ var subAssets = Directory.GetFiles(fullPath, "*.prefab");
+
+ for (var i = 0; i < subAssets.Length; i++)
+ {
+ FindFromFolder(basePath, subAssets[i].Substring(basePath.Length));
+ }
+ }
+ // File
+ else
+ {
+ var subGameObject = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(rootPath);
+
+ if (subGameObject != null)
+ {
+ FindFromGameObject(subGameObject.transform);
+ }
+ }
+ }
+#endif
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanPrefab.cs.meta b/Assets/Packages/Lean/Localization/Scripts/LeanPrefab.cs.meta
new file mode 100644
index 0000000..19da17c
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanPrefab.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a580c5d337407a9439b51424c8590dd3
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanSource.cs b/Assets/Packages/Lean/Localization/Scripts/LeanSource.cs
new file mode 100644
index 0000000..7480245
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanSource.cs
@@ -0,0 +1,30 @@
+using UnityEngine;
+using System.Collections.Generic;
+
+namespace Lean.Localization
+{
+ /// <summary>This is the interface used for all translation sources. When a translation source is built, it will populate the LeanLocalization class with its translation data.</summary>
+ public abstract class LeanSource : MonoBehaviour
+ {
+ public static LinkedList<LeanSource> Instances = new LinkedList<LeanSource>();
+
+ [System.NonSerialized]
+ private LinkedListNode<LeanSource> node;
+
+ public abstract void Compile(string primaryLanguage, string secondaryLanguage);
+
+ protected virtual void OnEnable()
+ {
+ node = Instances.AddLast(this);
+
+ LeanLocalization.DelayUpdateTranslations();
+ }
+
+ protected virtual void OnDisable()
+ {
+ Instances.Remove(node);
+
+ LeanLocalization.DelayUpdateTranslations();
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanSource.cs.meta b/Assets/Packages/Lean/Localization/Scripts/LeanSource.cs.meta
new file mode 100644
index 0000000..57802a9
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanSource.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f0fcf02b7e3c0524a84d768d686b3634
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanToken.cs b/Assets/Packages/Lean/Localization/Scripts/LeanToken.cs
new file mode 100644
index 0000000..c6ca7f8
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanToken.cs
@@ -0,0 +1,130 @@
+using UnityEngine;
+using System.Collections.Generic;
+using Lean.Common;
+#if UNITY_EDITOR
+using UnityEditor;
+
+namespace Lean.Localization
+{
+ [CustomEditor(typeof(LeanToken))]
+ public class LeanToken_Inspector : LeanInspector<LeanToken>
+ {
+ protected override void DrawInspector()
+ {
+ Draw("value");
+ }
+ }
+}
+#endif
+
+namespace Lean.Localization
+{
+ /// <summary>The class stores a token name (e.g. "AGE"), allowing it to be replaced with the token value (e.g. "20").
+ /// To use the token in your text, simply include the token name surrounded by braces (e.g. "I am {AGE} years old!")</summary>
+ [ExecuteInEditMode]
+ [HelpURL(LeanLocalization.HelpUrlPrefix + "LeanToken")]
+ [AddComponentMenu(LeanLocalization.ComponentPathPrefix + "Token")]
+ public class LeanToken : LeanSource
+ {
+ [SerializeField]
+ private string value;
+
+ [System.NonSerialized]
+ private HashSet<LeanLocalizedBehaviour> behaviours;
+
+ [System.NonSerialized]
+ private static HashSet<LeanLocalizedBehaviour> tempBehaviours = new HashSet<LeanLocalizedBehaviour>();
+
+ public string Value
+ {
+ set
+ {
+ if (this.value != value)
+ {
+ this.value = value;
+
+ if (behaviours != null)
+ {
+ tempBehaviours.Clear();
+
+ tempBehaviours.UnionWith(behaviours);
+
+ foreach (var behaviour in tempBehaviours)
+ {
+ behaviour.UpdateLocalization();
+ }
+ }
+ }
+ }
+
+ get
+ {
+ return value;
+ }
+ }
+
+ public void SetValue(float value)
+ {
+ Value = value.ToString();
+ }
+
+ public void SetValue(string value)
+ {
+ Value = value;
+ }
+
+ public void SetValue(int value)
+ {
+ Value = value.ToString();
+ }
+
+ public void Register(LeanLocalizedBehaviour behaviour)
+ {
+ if (behaviour != null)
+ {
+ if (behaviours == null)
+ {
+ behaviours = new HashSet<LeanLocalizedBehaviour>();
+ }
+
+ behaviours.Add(behaviour);
+ }
+ }
+
+ public void Unregister(LeanLocalizedBehaviour behaviour)
+ {
+ if (behaviours != null)
+ {
+ behaviours.Remove(behaviour);
+ }
+ }
+
+ public void UnregisterAll()
+ {
+ if (behaviours != null)
+ {
+ foreach (var behaviour in behaviours)
+ {
+ behaviour.Unregister(this);
+ }
+
+ behaviours.Clear();
+ }
+ }
+
+ public override void Compile(string primaryLanguage, string secondaryLanguage)
+ {
+ if (string.IsNullOrEmpty(name) == false)
+ {
+ LeanLocalization.CurrentTokens.Add(name, this);
+ }
+ }
+
+ protected override void OnDisable()
+ {
+ base.OnDisable();
+
+ UnregisterAll();
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanToken.cs.meta b/Assets/Packages/Lean/Localization/Scripts/LeanToken.cs.meta
new file mode 100644
index 0000000..ccef956
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanToken.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6b00b1c461a103b4898dcc53ee709d22
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanTranslation.cs b/Assets/Packages/Lean/Localization/Scripts/LeanTranslation.cs
new file mode 100644
index 0000000..5fc5e18
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanTranslation.cs
@@ -0,0 +1,191 @@
+using UnityEngine;
+using System.Collections.Generic;
+
+namespace Lean.Localization
+{
+ /// <summary>This contains the translated value for the current language, and other associated data.</summary>
+ public class LeanTranslation
+ {
+ public struct Entry
+ {
+ public string Language;
+
+ public Object Owner;
+ }
+
+ /// <summary>The name of this translation.</summary>
+ public string Name { get { return name; } } [SerializeField] private string name;
+
+ /// <summary>The data of this translation (e.g. string or Object).
+ /// NOTE: This is a System.Object, so you must correctly cast it back before use.</summary>
+ public object Data;
+
+ /// <summary>If Data has been filled with data for the primary language, then this will be set to true.</summary>
+ public bool Primary;
+
+ /// <summary>This stores a list of all LeanSource instances that are currently managing the current value of this translation in the current language.
+ /// NOTE: If this is empty then no LeanSource of this name is localized for the current language.</summary>
+ public List<Entry> Entries { get { return entries; } } private List<Entry> entries = new List<Entry>();
+
+ private static bool buffering;
+
+ private static System.Text.StringBuilder current = new System.Text.StringBuilder();
+
+ private static System.Text.StringBuilder buffer = new System.Text.StringBuilder();
+
+ private static List<LeanToken> tokens = new List<LeanToken>();
+
+ public LeanTranslation(string newName)
+ {
+ name = newName;
+ }
+
+ public void Register(string language, Object owner)
+ {
+ var entry = new Entry();
+
+ entry.Language = language;
+ entry.Owner = owner;
+
+ entries.Add(entry);
+ }
+
+ public void Clear()
+ {
+ Data = null;
+ Primary = false;
+
+ entries.Clear();
+ }
+
+ public int LanguageCount(string language)
+ {
+ var total = 0;
+
+ for (var i = entries.Count - 1; i >= 0; i--)
+ {
+ if (entries[i].Language == language)
+ {
+ total += 1;
+ }
+ }
+
+ return total;
+ }
+
+ /// <summary>This returns Text with all tokens substituted using the LeanLocalization.Tokens list.</summary>
+ public static string FormatText(string rawText, string currentText, LeanLocalizedBehaviour behaviour)
+ {
+ if (string.IsNullOrEmpty(currentText) == true)
+ {
+ currentText = rawText;
+ }
+
+ if (rawText != null)
+ {
+ current.Length = 0;
+ buffer.Length = 0;
+ tokens.Clear();
+
+ for (var i = 0; i < rawText.Length; i++)
+ {
+ var rawChar = rawText[i];
+
+ if (rawChar == '{')
+ {
+ if (buffering == true)
+ {
+ buffering = false;
+
+ buffer.Length = 0;
+ }
+ else
+ {
+ buffering = true;
+ }
+ }
+ else if (rawChar == '}')
+ {
+ if (buffering == true)
+ {
+ if (buffer.Length > 0)
+ {
+ var token = default(LeanToken);
+
+ if (buffer.Length > 0 && LeanLocalization.CurrentTokens.TryGetValue(buffer.ToString(), out token) == true) // TODO: Avoid ToString here?
+ {
+ current.Append(token.Value);
+
+ tokens.Add(token);
+ }
+ else
+ {
+ current.Append('{').Append(buffer).Append('}');
+ }
+
+ buffer.Length = 0;
+ }
+
+ buffering = false;
+ }
+ }
+ else
+ {
+ if (buffering == true)
+ {
+ buffer.Append(rawChar);
+ }
+ else
+ {
+ current.Append(rawChar);
+ }
+ }
+ }
+
+ if (Match(currentText, current) == false)
+ {
+ if (behaviour != null)
+ {
+ behaviour.UnregisterAll();
+ }
+
+ for (var i = tokens.Count - 1; i >= 0; i--)
+ {
+ var token = tokens[i];
+
+ token.Register(behaviour);
+
+ behaviour.Register(token);
+ }
+
+ return current.ToString();
+ }
+ }
+
+ return currentText;
+ }
+
+ private static bool Match(string a, System.Text.StringBuilder b)
+ {
+ if (a == null && b.Length > 0)
+ {
+ return false;
+ }
+
+ if (a.Length != b.Length)
+ {
+ return false;
+ }
+
+ for (var i = 0; i < a.Length; i++)
+ {
+ if (a[i] != b[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanTranslation.cs.meta b/Assets/Packages/Lean/Localization/Scripts/LeanTranslation.cs.meta
new file mode 100644
index 0000000..6f16358
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanTranslation.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 262535ccf26608d488744fb304c45389
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanTranslationNameAttribute.cs b/Assets/Packages/Lean/Localization/Scripts/LeanTranslationNameAttribute.cs
new file mode 100644
index 0000000..740d43d
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanTranslationNameAttribute.cs
@@ -0,0 +1,54 @@
+using UnityEngine;
+#if UNITY_EDITOR
+using UnityEditor;
+
+namespace Lean.Localization
+{
+ [CustomPropertyDrawer(typeof(LeanTranslationNameAttribute))]
+ public class LeanTranslationNameDrawer : PropertyDrawer
+ {
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
+ {
+ var left = position; left.xMax -= 40;
+ var right = position; right.xMin = left.xMax + 2;
+ var color = GUI.color;
+
+ if (LeanLocalization.CurrentTranslations.ContainsKey(property.stringValue) == false)
+ {
+ GUI.color = Color.red;
+ }
+
+ EditorGUI.PropertyField(left, property);
+
+ GUI.color = color;
+
+ if (GUI.Button(right, "List") == true)
+ {
+ var menu = new GenericMenu();
+
+ foreach (var translationName in LeanLocalization.CurrentTranslations.Keys)
+ {
+ menu.AddItem(new GUIContent(translationName), property.stringValue == translationName, () => { property.stringValue = translationName; property.serializedObject.ApplyModifiedProperties(); });
+ }
+
+ if (menu.GetItemCount() > 0)
+ {
+ menu.DropDown(right);
+ }
+ else
+ {
+ Debug.LogWarning("Your scene doesn't contain any phrases, so the phrase name list couldn't be created.");
+ }
+ }
+ }
+ }
+}
+#endif
+
+namespace Lean.Localization
+{
+ /// <summary>This attribute allows you to select a translation from all the localizations in the scene.</summary>
+ public class LeanTranslationNameAttribute : PropertyAttribute
+ {
+ }
+} \ No newline at end of file
diff --git a/Assets/Packages/Lean/Localization/Scripts/LeanTranslationNameAttribute.cs.meta b/Assets/Packages/Lean/Localization/Scripts/LeanTranslationNameAttribute.cs.meta
new file mode 100644
index 0000000..e63dbdc
--- /dev/null
+++ b/Assets/Packages/Lean/Localization/Scripts/LeanTranslationNameAttribute.cs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: cdfebeb32b399cb498725e695b85d6eb
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData: