Unity 国际化 多语言设置

时间:2023-03-08 19:48:20
Unity 国际化 多语言设置

很多游戏中都有语言设置选项,NGUI插件中自带了国际化脚本,但是灵活性较低,而且目前项目是UGUI,以下是修改后,以便记录。

Localization和NGUI中用法一样,挂在在一个不销毁的游戏物体上,并设置当前语言,及所有语言的陪标

  1. //----------------------------------------------
  2. //----------------------------------------------
  3. using UnityEngine;
  4. using System.Collections.Generic;
  5. using UnityEngine.UI;
  6. public class Localization : MonoBehaviour
  7. {
  8. static Localization mInst;
  9. static public Localization instance
  10. {
  11. get
  12. {
  13. if (mInst == null)
  14. {
  15. mInst = Object.FindObjectOfType(typeof(Localization)) as Localization;
  16. if (mInst == null)
  17. {
  18. GameObject go = new GameObject("_Localization");
  19. DontDestroyOnLoad(go);
  20. mInst = go.AddComponent<Localization>();
  21. }
  22. }
  23. return mInst;
  24. }
  25. }
  26. public string startingLanguage;
  27. public TextAsset[] languages;
  28. Dictionary<int, string> mDictionary = new Dictionary<int, string>();
  29. string mLanguage;
  30. public string currentLanguage
  31. {
  32. get
  33. {
  34. //if (string.IsNullOrEmpty(mLanguage))
  35. {
  36. currentLanguage = PlayerPrefs.GetString("Language");
  37. if (string.IsNullOrEmpty(mLanguage))
  38. {
  39. currentLanguage = startingLanguage;
  40. if (string.IsNullOrEmpty(mLanguage) && (languages != null && languages.Length > 0))
  41. {
  42. currentLanguage = languages[0].name;
  43. }
  44. }
  45. }
  46. return mLanguage;
  47. }
  48. set
  49. {
  50. if (mLanguage != value)
  51. {
  52. startingLanguage = value;
  53. if (!string.IsNullOrEmpty(value))
  54. {
  55. if (languages != null)
  56. {
  57. for (int i = 0, imax = languages.Length; i < imax; ++i)
  58. {
  59. TextAsset asset = languages[i];
  60. if (asset != null && asset.name == value)
  61. {
  62. Load(asset);
  63. return;
  64. }
  65. }
  66. }
  67. TextAsset txt = Resources.Load(value, typeof(TextAsset)) as TextAsset;
  68. if (txt != null)
  69. {
  70. Load(txt);
  71. return;
  72. }
  73. }
  74. mDictionary.Clear();
  75. PlayerPrefs.DeleteKey("Language");
  76. }
  77. }
  78. }
  79. /// <summary>
  80. /// Determine the starting language.
  81. /// </summary>
  82. void Awake() { if (mInst == null) { mInst = this; DontDestroyOnLoad(gameObject); } else Destroy(gameObject); }
  83. /// <summary>
  84. /// Start with the specified starting language.
  85. /// </summary>
  86. void Start() { if (!string.IsNullOrEmpty(startingLanguage)) currentLanguage = startingLanguage; }
  87. /// <summary>
  88. /// Oddly enough... sometimes if there is no OnEnable function in Localization, it can get the Awake call after UILocalize's OnEnable.
  89. /// </summary>
  90. void OnEnable() { if (mInst == null) mInst = this; }
  91. /// <summary>
  92. /// Remove the instance reference.
  93. /// </summary>
  94. void OnDestroy() { if (mInst == this) mInst = null; }
  95. /// <summary>
  96. /// Load the specified asset and activate the localization.
  97. /// </summary>
  98. void Load(TextAsset asset)
  99. {
  100. mLanguage = asset.name;
  101. PlayerPrefs.SetString("Language", mLanguage);
  102. ByteReader reader = new ByteReader(asset);
  103. mDictionary = reader.ReadDictionary();
  104. // Broadcast("OnLocalize");
  105. }
  106. public string Get(int key)
  107. {
  108. string val;
  109. return (mDictionary.TryGetValue(key, out val)) ? val : null;
  110. }
  111. }

ByteReader数据解析类,不多余解释

  1. //----------------------------------------------
  2. //            NGUI: Next-Gen UI kit
  3. // Copyright © 2011-2012 Tasharen Entertainment
  4. //----------------------------------------------
  5. using UnityEngine;
  6. using System.Text;
  7. using System.Collections.Generic;
  8. /// <summary>
  9. /// MemoryStream.ReadLine has an interesting oddity: it doesn't always advance the stream's position by the correct amount:
  10. /// http://social.msdn.microsoft.com/Forums/en-AU/Vsexpressvcs/thread/b8f7837b-e396-494e-88e1-30547fcf385f
  11. /// Solution? Custom line reader with the added benefit of not having to use streams at all.
  12. /// </summary>
  13. public class ByteReader
  14. {
  15. byte[] mBuffer;
  16. int mOffset = 0;
  17. public ByteReader(byte[] bytes) { mBuffer = bytes; }
  18. public ByteReader(TextAsset asset) { mBuffer = asset.bytes; }
  19. /// <summary>
  20. /// Whether the buffer is readable.
  21. /// </summary>
  22. public bool canRead { get { return (mBuffer != null && mOffset < mBuffer.Length); } }
  23. /// <summary>
  24. /// Read a single line from the buffer.
  25. /// </summary>
  26. static string ReadLine(byte[] buffer, int start, int count)
  27. {
  28. #if UNITY_FLASH
  29. // Encoding.UTF8 is not supported in Flash :(
  30. StringBuilder sb = new StringBuilder();
  31. int max = start + count;
  32. for (int i = start; i < max; ++i)
  33. {
  34. byte byte0 = buffer[i];
  35. if ((byte0 & 128) == 0)
  36. {
  37. // If an UCS fits 7 bits, its coded as 0xxxxxxx. This makes ASCII character represented by themselves
  38. sb.Append((char)byte0);
  39. }
  40. else if ((byte0 & 224) == 192)
  41. {
  42. // If an UCS fits 11 bits, it is coded as 110xxxxx 10xxxxxx
  43. if (++i == count) break;
  44. byte byte1 = buffer[i];
  45. int ch = (byte0 & 31) << 6;
  46. ch |= (byte1 & 63);
  47. sb.Append((char)ch);
  48. }
  49. else if ((byte0 & 240) == 224)
  50. {
  51. // If an UCS fits 16 bits, it is coded as 1110xxxx 10xxxxxx 10xxxxxx
  52. if (++i == count) break;
  53. byte byte1 = buffer[i];
  54. if (++i == count) break;
  55. byte byte2 = buffer[i];
  56. if (byte0 == 0xEF && byte1 == 0xBB && byte2 == 0xBF)
  57. {
  58. // Byte Order Mark -- generally the first 3 bytes in a Windows-saved UTF-8 file. Skip it.
  59. }
  60. else
  61. {
  62. int ch = (byte0 & 15) << 12;
  63. ch |= (byte1 & 63) << 6;
  64. ch |= (byte2 & 63);
  65. sb.Append((char)ch);
  66. }
  67. }
  68. else if ((byte0 & 248) == 240)
  69. {
  70. // If an UCS fits 21 bits, it is coded as 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
  71. if (++i == count) break;
  72. byte byte1 = buffer[i];
  73. if (++i == count) break;
  74. byte byte2 = buffer[i];
  75. if (++i == count) break;
  76. byte byte3 = buffer[i];
  77. int ch = (byte0 & 7) << 18;
  78. ch |= (byte1 & 63) << 12;
  79. ch |= (byte2 & 63) << 6;
  80. ch |= (byte3 & 63);
  81. sb.Append((char)ch);
  82. }
  83. }
  84. return sb.ToString();
  85. #else
  86. return Encoding.UTF8.GetString(buffer, start, count);
  87. #endif
  88. }
  89. /// <summary>
  90. /// Read a single line from the buffer.
  91. /// </summary>
  92. public string ReadLine()
  93. {
  94. int max = mBuffer.Length;
  95. // Skip empty characters
  96. while (mOffset < max && mBuffer[mOffset] < 32) ++mOffset;
  97. int end = mOffset;
  98. if (end < max)
  99. {
  100. for (;;)
  101. {
  102. if (end < max)
  103. {
  104. int ch = mBuffer[end++];
  105. if (ch != '\n' && ch != '\r') continue;
  106. }
  107. else ++end;
  108. string line = ReadLine(mBuffer, mOffset, end - mOffset - 1);
  109. mOffset = end;
  110. return line;
  111. }
  112. }
  113. mOffset = max;
  114. return null;
  115. }
  116. /// <summary>
  117. /// Assume that the entire file is a collection of key/value pairs.
  118. /// </summary>
  119. public Dictionary<int, string> ReadDictionary()
  120. {
  121. Dictionary<int, string> dict = new Dictionary<int, string>();
  122. char[] separator = new char[] { '=' };
  123. while (canRead)
  124. {
  125. string line = ReadLine();
  126. if (line == null) break;
  127. #if UNITY_FLASH
  128. string[] split = line.Split(separator, System.StringSplitOptions.RemoveEmptyEntries);
  129. #else
  130. string[] split = line.Split(separator, 2, System.StringSplitOptions.RemoveEmptyEntries);
  131. #endif
  132. if (split.Length == 2)
  133. {
  134. int key = int.Parse(split[0].Trim());
  135. string val = split[1].Trim();
  136. dict[key] = val;
  137. }
  138. }
  139. return dict;
  140. }
  141. }

将UILocalize脚本挂在在Text上或者Image上,就是需要多语言切换的UI上

  1. //----------------------------------------------
  2. //----------------------------------------------
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. public class UILocalize : MonoBehaviour
  6. {
  7. /// <summary>
  8. /// Localization key.
  9. /// </summary>
  10. public string Atlas;
  11. public string key;
  12. string mLanguage;
  13. bool mStarted = false;
  14. /// <summary>
  15. /// This function is called by the Localization manager via a broadcast SendMessage.
  16. /// </summary>
  17. void OnLocalize(Localization loc) { if (mLanguage != loc.currentLanguage) Localize(); }
  18. /// <summary>
  19. /// Localize the widget on enable, but only if it has been started already.
  20. /// </summary>
  21. void OnEnable() { if (mStarted && Localization.instance != null) Localize(); }
  22. /// <summary>
  23. /// Localize the widget on start.
  24. /// </summary>
  25. void Start()
  26. {
  27. mStarted = true;
  28. if (Localization.instance != null) Localize();
  29. }
  30. /// <summary>
  31. /// Force-localize the widget.
  32. /// </summary>
  33. public void Localize(string key = null)
  34. {
  35. if (key == null)
  36. key = this.key;
  37. Localization loc = Localization.instance;
  38. MaskableGraphic w = GetComponent<MaskableGraphic>();
  39. Text lbl = w as Text;
  40. Image sp = w as Image;
  41. // If no localization key has been specified, use the label's text as the key
  42. if (string.IsNullOrEmpty(mLanguage) && string.IsNullOrEmpty(key) && lbl != null)
  43. key = lbl.text;
  44. // If we still don't have a key, use the widget's name
  45. string val = string.IsNullOrEmpty(key) ? w.name : loc.Get(int.Parse(key));
  46. if (val != null)
  47. {
  48. if (lbl != null)
  49. {
  50. lbl.text = val;
  51. }
  52. else if (sp != null)
  53. {
  54. if (Atlas == null)
  55. return;
  56. sp.sprite = DoRo.Manager.LoadManager.Instance.LoadSprite(Atlas, val);
  57. }
  58. }
  59. mLanguage = loc.currentLanguage;
  60. }
  61. }