diff --git a/Assets/Scripts/Localization/LocalizationManager.cs b/Assets/Scripts/Localization/LocalizationManager.cs
index 3fb13bb..0c4118e 100644
--- a/Assets/Scripts/Localization/LocalizationManager.cs
+++ b/Assets/Scripts/Localization/LocalizationManager.cs
@@ -487,6 +487,17 @@ public class LocalizationManager : MonoBehaviour
{ "{0}与{1}边界间距为{2}M,小于200M", new[] { "{0}与{1}边界间距为{2}M,小于200M", "Distance between {0} and {1} boundary is {2}M, less than 200M", "Distance entre {0} et {1} : {2}M, inférieure à 200M", "Расстояние между {0} и {1}: {2}м, менее 200м" } },
{ "{0}与{1}边界间距为{2}M,小于100M", new[] { "{0}与{1}边界间距为{2}M,小于100M", "Distance between {0} and {1} boundary is {2}M, less than 100M", "Distance entre {0} et {1} : {2}M, inférieure à 100M", "Расстояние между {0} и {1}: {2}м, менее 100м" } },
{ "{0}距离水源距离为{1}M,超出100M", new[] { "{0}距离水源距离为{1}M,超出100M", "{0} distance to water source is {1}M, exceeds 100M", "Distance de {0} à la source d'eau : {1}M, supérieure à 100M", "Расстояние от {0} до источника воды {1}м, более 100м" } },
+
+ // VR controller tooltips (VRTK_ControllerTooltips)
+ { "展/收", new[] { "展/收", "Deploy/Stow", "Déployer/Ranger", "Развернуть/Свернуть" } },
+ { "选中车辆", new[] { "选中车辆", "Select Vehicle", "Sélectionner le véhicule", "Выбрать машину" } },
+ { "移动", new[] { "移动", "Move", "Déplacer", "Движение" } },
+ { "射线", new[] { "射线", "Ray", "Rayon", "Луч" } },
+ { "移动车辆", new[] { "移动车辆", "Move Vehicle", "Déplacer le véhicule", "Переместить машину" } },
+ { "选择车辆", new[] { "选择车辆", "Select Vehicle", "Sélectionner le véhicule", "Выбрать машину" } },
+ { "触发物体", new[] { "触发物体", "Trigger Object", "Activer l'objet", "Активировать объект" } },
+ { "抓取物体", new[] { "抓取物体", "Grab Object", "Saisir l'objet", "Захватить объект" } },
+ { "传送", new[] { "传送", "Teleport", "Téléportation", "Телепорт" } },
};
[SerializeField] private int language;
diff --git a/Assets/Scripts/Managers/VRControllerTooltipLocalizer.cs b/Assets/Scripts/Managers/VRControllerTooltipLocalizer.cs
new file mode 100644
index 0000000..4907bda
--- /dev/null
+++ b/Assets/Scripts/Managers/VRControllerTooltipLocalizer.cs
@@ -0,0 +1,141 @@
+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);
+ }
+}
diff --git a/Assets/Scripts/Managers/VRControllerTooltipLocalizer.cs.meta b/Assets/Scripts/Managers/VRControllerTooltipLocalizer.cs.meta
new file mode 100644
index 0000000..986dc8e
--- /dev/null
+++ b/Assets/Scripts/Managers/VRControllerTooltipLocalizer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b6e286fc5954c4045bb66e03d68ba7bb
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Managers/VRManager.cs b/Assets/Scripts/Managers/VRManager.cs
index 8a2f04c..f0a0ddf 100644
--- a/Assets/Scripts/Managers/VRManager.cs
+++ b/Assets/Scripts/Managers/VRManager.cs
@@ -22,6 +22,7 @@ public class VRManager : IVRInput
{
SDKManager = GameObject.Instantiate(Resources.Load(ResourcesManager.GetPath(vrtkPrefabName))).GetComponent();
SDKManager.transform.position = postion;
+ SDKManager.gameObject.AddComponent();
SDKManager.LoadedSetupChanged += SDKManager_LoadedSetupChanged;
VRTK_Logger.instance.throwExceptions = false;
//leftInput = SDKManager.scriptAliasLeftController.GetComponent();