perf: 优化语言切换性能并修复实时翻译丢失 - 预构建O(1)查找字典、缓存组件检查和字体状态、改用FindObjectsOfType、调整SetLanguage顺序为LanguageChanged先于RefreshAll
This commit is contained in:
parent
33a1681f3d
commit
de46915c70
@ -18,14 +18,23 @@ public class LocalizationManager : MonoBehaviour
|
||||
public string source;
|
||||
public string localized;
|
||||
public object[] formatArgs;
|
||||
public bool skipDropdown; // cached: this Text is a Dropdown caption/item
|
||||
public bool skipInputField; // cached: this Text is inside an InputField
|
||||
public bool fontApplied; // cached: font has been set at least once
|
||||
}
|
||||
|
||||
private class DropdownState
|
||||
{
|
||||
public List<string> sources = new List<string>();
|
||||
public List<string> localized = new List<string>();
|
||||
public bool fontApplied;
|
||||
}
|
||||
|
||||
// Pre-built lookup dictionaries for O(1) translation
|
||||
private Dictionary<string, string> reverseTable;
|
||||
private Dictionary<string, string[]> normalizedTable;
|
||||
private Dictionary<string, string> normalizedReverseTable;
|
||||
|
||||
public const string PreferenceKey = "UI.Language";
|
||||
private const string FontResourcePath = "Localization/NotoSansCJKsc-Regular";
|
||||
|
||||
@ -542,6 +551,37 @@ public class LocalizationManager : MonoBehaviour
|
||||
instance = this;
|
||||
language = Mathf.Clamp(PlayerPrefs.GetInt(PreferenceKey, 0), 0, 3);
|
||||
font = Resources.Load<Font>(FontResourcePath);
|
||||
BuildLookupTables();
|
||||
}
|
||||
|
||||
private void BuildLookupTables()
|
||||
{
|
||||
reverseTable = new Dictionary<string, string>();
|
||||
normalizedTable = new Dictionary<string, string[]>();
|
||||
normalizedReverseTable = new Dictionary<string, string>();
|
||||
|
||||
foreach (var pair in table)
|
||||
{
|
||||
// Normalized key → values (for fuzzy Translate)
|
||||
string nk = System.Text.RegularExpressions.Regex.Replace(pair.Key, @"\s+", " ").Trim();
|
||||
if (!normalizedTable.ContainsKey(nk))
|
||||
normalizedTable[nk] = pair.Value;
|
||||
|
||||
if (pair.Value == null) continue;
|
||||
for (int i = 0; i < pair.Value.Length; i++)
|
||||
{
|
||||
if (pair.Value[i] == null) continue;
|
||||
|
||||
// Exact value → source key (for exact GetSourceText)
|
||||
if (!reverseTable.ContainsKey(pair.Value[i]))
|
||||
reverseTable[pair.Value[i]] = pair.Key;
|
||||
|
||||
// Normalized value → source key (for fuzzy GetSourceText)
|
||||
string nv = System.Text.RegularExpressions.Regex.Replace(pair.Value[i], @"\s+", " ").Trim();
|
||||
if (!normalizedReverseTable.ContainsKey(nv))
|
||||
normalizedReverseTable[nv] = pair.Key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Start()
|
||||
@ -553,13 +593,10 @@ public class LocalizationManager : MonoBehaviour
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
// Covers canvases instantiated after the current frame and avoids duplicate bars.
|
||||
LocalizationLanguageTestBar.EnsureForActiveCanvases();
|
||||
|
||||
// Periodically refresh localization for dynamically loaded UI (e.g. student panels).
|
||||
if (++refreshFrameCounter >= 30)
|
||||
if (++refreshFrameCounter >= 60)
|
||||
{
|
||||
refreshFrameCounter = 0;
|
||||
LocalizationLanguageTestBar.EnsureForActiveCanvases();
|
||||
RefreshAll();
|
||||
}
|
||||
}
|
||||
@ -570,8 +607,10 @@ public class LocalizationManager : MonoBehaviour
|
||||
instance.language = Mathf.Clamp(value, 0, 3);
|
||||
PlayerPrefs.SetInt(PreferenceKey, instance.language);
|
||||
PlayerPrefs.Save();
|
||||
instance.RefreshAll();
|
||||
// Fire LanguageChanged FIRST so handlers rebuild content (table rows, operation messages, etc.),
|
||||
// then RefreshAll catches all texts including the newly created ones.
|
||||
if (LanguageChanged != null) LanguageChanged();
|
||||
instance.RefreshAll();
|
||||
}
|
||||
|
||||
public static string Get(string source)
|
||||
@ -700,10 +739,10 @@ public class LocalizationManager : MonoBehaviour
|
||||
|
||||
public void RefreshAll()
|
||||
{
|
||||
Text[] texts = Resources.FindObjectsOfTypeAll<Text>();
|
||||
Text[] texts = FindObjectsOfType<Text>();
|
||||
for (int i = 0; i < texts.Length; i++) Localize(texts[i]);
|
||||
|
||||
Dropdown[] dropdowns = Resources.FindObjectsOfTypeAll<Dropdown>();
|
||||
Dropdown[] dropdowns = FindObjectsOfType<Dropdown>();
|
||||
for (int i = 0; i < dropdowns.Length; i++) Localize(dropdowns[i]);
|
||||
}
|
||||
|
||||
@ -716,14 +755,11 @@ public class LocalizationManager : MonoBehaviour
|
||||
if (table.TryGetValue(source, out values) && values != null && language < values.Length && values[language] != null)
|
||||
return values[language];
|
||||
|
||||
// Fuzzy: try normalized lookup
|
||||
// Fuzzy: O(1) normalized lookup
|
||||
string normalized = System.Text.RegularExpressions.Regex.Replace(source, @"\s+", " ").Trim();
|
||||
foreach (KeyValuePair<string, string[]> pair in table)
|
||||
{
|
||||
string nk = System.Text.RegularExpressions.Regex.Replace(pair.Key, @"\s+", " ").Trim();
|
||||
if (nk == normalized && pair.Value != null && language < pair.Value.Length && pair.Value[language] != null)
|
||||
return pair.Value[language];
|
||||
}
|
||||
string[] normValues;
|
||||
if (normalizedTable.TryGetValue(normalized, out normValues) && normValues != null && language < normValues.Length && normValues[language] != null)
|
||||
return normValues[language];
|
||||
|
||||
return source;
|
||||
}
|
||||
@ -732,31 +768,13 @@ public class LocalizationManager : MonoBehaviour
|
||||
{
|
||||
if (string.IsNullOrEmpty(value) || table.ContainsKey(value)) return value;
|
||||
|
||||
// Exact match
|
||||
foreach (KeyValuePair<string, string[]> pair in table)
|
||||
{
|
||||
if (pair.Value == null || pair.Value.Length <= 1) continue;
|
||||
for (int i = 1; i < pair.Value.Length; i++)
|
||||
{
|
||||
if (object.ReferenceEquals(pair.Value[i], value)) return pair.Key;
|
||||
if (pair.Value[i] != null && pair.Value[i] == value) return pair.Key;
|
||||
}
|
||||
}
|
||||
// O(1) exact reverse lookup
|
||||
string key;
|
||||
if (reverseTable.TryGetValue(value, out key)) return key;
|
||||
|
||||
// Fuzzy match: normalize whitespace (newlines, tabs, multiple spaces → single space)
|
||||
// O(1) fuzzy reverse lookup
|
||||
string normalizedValue = System.Text.RegularExpressions.Regex.Replace(value, @"\s+", " ").Trim();
|
||||
foreach (KeyValuePair<string, string[]> pair in table)
|
||||
{
|
||||
if (pair.Value == null || pair.Value.Length <= 1) continue;
|
||||
string normalizedKey = System.Text.RegularExpressions.Regex.Replace(pair.Key, @"\s+", " ").Trim();
|
||||
if (normalizedKey == normalizedValue) return pair.Key;
|
||||
for (int i = 1; i < pair.Value.Length; i++)
|
||||
{
|
||||
if (pair.Value[i] == null) continue;
|
||||
string normalizedVal = System.Text.RegularExpressions.Regex.Replace(pair.Value[i], @"\s+", " ").Trim();
|
||||
if (normalizedVal == normalizedValue) return pair.Key;
|
||||
}
|
||||
}
|
||||
if (normalizedReverseTable.TryGetValue(normalizedValue, out key)) return key;
|
||||
|
||||
return value;
|
||||
}
|
||||
@ -765,34 +783,40 @@ public class LocalizationManager : MonoBehaviour
|
||||
{
|
||||
if (text == null || !text.gameObject.scene.IsValid()) return;
|
||||
|
||||
// Dropdown caption and item texts are handled by Localize(Dropdown).
|
||||
Dropdown dropdown = text.GetComponentInParent<Dropdown>();
|
||||
if (dropdown != null && (dropdown.captionText == text || dropdown.itemText == text))
|
||||
return;
|
||||
int id = text.GetInstanceID();
|
||||
TextState state;
|
||||
if (!textStates.TryGetValue(id, out state))
|
||||
{
|
||||
// First encounter: do expensive checks once and cache
|
||||
Dropdown dropdown = text.GetComponentInParent<Dropdown>();
|
||||
InputField input = text.GetComponentInParent<InputField>();
|
||||
state = new TextState
|
||||
{
|
||||
source = text.text,
|
||||
localized = text.text,
|
||||
skipDropdown = dropdown != null && (dropdown.captionText == text || dropdown.itemText == text),
|
||||
skipInputField = input != null
|
||||
};
|
||||
textStates.Add(id, state);
|
||||
}
|
||||
else if (text.text != state.localized)
|
||||
{
|
||||
// Text was externally changed; update source but keep cached skip flags
|
||||
state.source = GetSourceText(text.text);
|
||||
state.formatArgs = null;
|
||||
}
|
||||
|
||||
// InputField children are managed by their owning panels to avoid
|
||||
// interfering with user input and placeholder rendering.
|
||||
if (text.GetComponentInParent<InputField>() != null) return;
|
||||
if (state.skipDropdown) return;
|
||||
if (state.skipInputField) return;
|
||||
|
||||
if (font != null)
|
||||
// Apply font only once per Text
|
||||
if (font != null && !state.fontApplied)
|
||||
{
|
||||
Color savedColor = text.color;
|
||||
text.font = font;
|
||||
text.color = savedColor;
|
||||
text.verticalOverflow = VerticalWrapMode.Overflow;
|
||||
}
|
||||
|
||||
int id = text.GetInstanceID();
|
||||
TextState state;
|
||||
if (!textStates.TryGetValue(id, out state))
|
||||
{
|
||||
state = new TextState { source = text.text, localized = text.text };
|
||||
textStates.Add(id, state);
|
||||
}
|
||||
else if (text.text != state.localized)
|
||||
{
|
||||
state.source = GetSourceText(text.text);
|
||||
state.formatArgs = null;
|
||||
state.fontApplied = true;
|
||||
}
|
||||
|
||||
state.localized = state.formatArgs == null
|
||||
@ -1041,7 +1065,7 @@ public class LocalizationManager : MonoBehaviour
|
||||
|
||||
|
||||
|
||||
if (font != null)
|
||||
if (font != null && !state.fontApplied)
|
||||
{
|
||||
Text[] texts = dropdown.GetComponentsInChildren<Text>(true);
|
||||
for (int i = 0; i < texts.Length; i++)
|
||||
@ -1051,6 +1075,7 @@ public class LocalizationManager : MonoBehaviour
|
||||
texts[i].color = savedColor;
|
||||
texts[i].verticalOverflow = VerticalWrapMode.Overflow;
|
||||
}
|
||||
state.fontApplied = true;
|
||||
}
|
||||
|
||||
dropdown.RefreshShownValue();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user