using UnityEngine; using UnityEngine.UI; using VRTK; /// /// Localizes VRTK controller tooltip text at runtime. /// Caches original Chinese texts from prefab fields, then updates /// via ResetTooltip() when the language changes. /// public class VRControllerTooltipLocalizer : MonoBehaviour { private class TooltipCache { public VRTK_ControllerTooltips component; public string triggerText; public string gripText; public string touchpadText; public string buttonOneText; public string buttonTwoText; public string startMenuText; } private TooltipCache[] caches; private bool initialized; void Start() { CacheTooltips(); if (initialized) ApplyLocalization(); LocalizationManager.LanguageChanged += OnLanguageChanged; } void OnDestroy() { LocalizationManager.LanguageChanged -= OnLanguageChanged; } private void OnLanguageChanged() { if (initialized) ApplyLocalization(); } private void CacheTooltips() { VRTK_ControllerTooltips[] tooltips = GetComponentsInChildren(true); if (tooltips == null || tooltips.Length == 0) { tooltips = FindObjectsOfType(); } if (tooltips == null || tooltips.Length == 0) return; caches = new TooltipCache[tooltips.Length]; for (int i = 0; i < tooltips.Length; i++) { caches[i] = new TooltipCache { component = tooltips[i], triggerText = tooltips[i].triggerText, gripText = tooltips[i].gripText, touchpadText = tooltips[i].touchpadText, buttonOneText = tooltips[i].buttonOneText, buttonTwoText = tooltips[i].buttonTwoText, startMenuText = tooltips[i].startMenuText }; } initialized = true; } private void ApplyLocalization() { Font font = LocalizationManager.UIFont; foreach (var cache in caches) { if (cache.component == null) continue; // Set all fields with localized text, then reset once cache.component.triggerText = LocalizeTooltipText(cache.triggerText); cache.component.gripText = LocalizeTooltipText(cache.gripText); cache.component.touchpadText = LocalizeTooltipText(cache.touchpadText); cache.component.buttonOneText = LocalizeTooltipText(cache.buttonOneText); cache.component.buttonTwoText = LocalizeTooltipText(cache.buttonTwoText); cache.component.startMenuText = LocalizeTooltipText(cache.startMenuText); cache.component.ResetTooltip(); // Apply localized font to tooltip Text components after ResetTooltip // so SetText() has already run (it doesn't touch font, only material) if (font != null) { VRTK_ObjectTooltip[] objTooltips = cache.component.GetComponentsInChildren(true); foreach (var ot in objTooltips) { ApplyFontRecursive(ot.transform, font); } } } } private void ApplyFontRecursive(Transform parent, Font font) { Text[] texts = parent.GetComponentsInChildren(true); foreach (var t in texts) { Color savedColor = t.color; t.font = font; t.color = savedColor; t.verticalOverflow = VerticalWrapMode.Overflow; } } /// /// Translate tooltip text that may contain \\n (literal backslash-n) /// or \n (actual newline) as multi-line separators. /// Each line is translated independently. /// private static string LocalizeTooltipText(string original) { if (string.IsNullOrEmpty(original)) return original; // VRTK uses literal \\n in prefab fields; SetText() converts to \n if (original.Contains("\\n")) { string[] parts = original.Split(new[] { "\\n" }, System.StringSplitOptions.None); for (int i = 0; i < parts.Length; i++) parts[i] = LocalizationManager.Get(parts[i].Trim()); return string.Join("\\n", parts); } // Handle actual newlines (in case text was already converted) if (original.Contains("\n")) { string[] parts = original.Split(new[] { "\n" }, System.StringSplitOptions.None); for (int i = 0; i < parts.Length; i++) parts[i] = LocalizationManager.Get(parts[i].Trim()); return string.Join("\n", parts); } return LocalizationManager.Get(original); } }