Fix issues with local settings tab localization

This commit is contained in:
Atony committed 2026-08-27 19:29:12 -05:00
1 parent 28a77f96cb
commit 48aff163f9
9 files changed
+110 -10

No files matched your search

+5
View File
@@ -39,4 +39,9 @@ public interface ILocalSetting
/// <param name="last">Whether it's the last on the row.</param> /// <param name="last">Whether it's the last on the row.</param>
/// <returns>The created setting's <see cref="GameObject"/>.</returns> /// <returns>The created setting's <see cref="GameObject"/>.</returns>
GameObject? CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last); GameObject? CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last);
/// <summary>
/// Used to refresh the setting's information, usually for localization.
/// </summary>
void RefreshOption();
} }
+19 -3
View File
@@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using BepInEx.Configuration; using BepInEx.Configuration;
using HarmonyLib; using HarmonyLib;
using MiraAPI.LocalSettings.SettingTypes;
using MiraAPI.Patches.LocalSettings; using MiraAPI.Patches.LocalSettings;
using MiraAPI.Translation; using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
@@ -53,6 +54,11 @@ public abstract class LocalSettingsTab(ConfigFile config)
/// </summary> /// </summary>
public List<ILocalSetting> Settings { get; } = []; public List<ILocalSetting> Settings { get; } = [];
/// <summary>
/// Gets a collection of <see cref="TextMeshPro"/>s alongside the locale keys for them.
/// </summary>
public Dictionary<TextMeshPro, string> Labels { get; } = [];
/// <summary> /// <summary>
/// Gets the list of <see cref="LocalSettingsButton"/>s. /// Gets the list of <see cref="LocalSettingsButton"/>s.
/// </summary> /// </summary>
@@ -74,12 +80,21 @@ public abstract class LocalSettingsTab(ConfigFile config)
} }
/// <summary> /// <summary>
/// Attempts to open the tab. /// Refreshes setting names upon opening the tab.
/// Override for custom behavior. /// Override for custom behavior.
/// </summary> /// </summary>
public virtual void Open() public virtual void Open()
{ {
OptionsMenuPatches.Instance?.OpenTabGroup(TabIndex); foreach (var setting in Settings)
{
setting.RefreshOption();
}
foreach (var label in Labels)
{
label.Key.text =
$"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\"><b>{label.Value.Translate()}</b></font>";
}
} }
/// <summary> /// <summary>
@@ -273,7 +288,7 @@ public abstract class LocalSettingsTab(ConfigFile config)
return tabButtonObject.gameObject; return tabButtonObject.gameObject;
} }
private static void CreateLabel(GameObject template, Transform parent, string text, ref float offset) private void CreateLabel(GameObject template, Transform parent, string text, ref float offset)
{ {
var label = Object.Instantiate(template, parent); var label = Object.Instantiate(template, parent);
label.transform.localPosition = new Vector3(0, 1.85f - offset); label.transform.localPosition = new Vector3(0, 1.85f - offset);
@@ -281,6 +296,7 @@ public abstract class LocalSettingsTab(ConfigFile config)
label.name = text; label.name = text;
var tmp = label.GetComponent<TextMeshPro>(); var tmp = label.GetComponent<TextMeshPro>();
Labels.Add(tmp, text);
tmp.text = $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\"><b>{text.Translate()}</b></font>"; tmp.text = $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\"><b>{text.Translate()}</b></font>";
tmp.alignment = TextAlignmentOptions.Center; tmp.alignment = TextAlignmentOptions.Center;
@@ -26,6 +26,10 @@ public class LocalEnumSetting : LocalSettingBase<int>
/// </summary> /// </summary>
public string[] Values { get; } public string[] Values { get; }
private SpriteRenderer _highlight { get; set; }
private TextMeshPro _btnText { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LocalEnumSetting"/> class. /// Initializes a new instance of the <see cref="LocalEnumSetting"/> class.
/// </summary> /// </summary>
@@ -54,6 +58,7 @@ public class LocalEnumSetting : LocalSettingBase<int>
{ {
var button = Object.Instantiate(toggle, parent).GetComponent<PassiveButton>(); var button = Object.Instantiate(toggle, parent).GetComponent<PassiveButton>();
var tmp = button.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>(); var tmp = button.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>();
_btnText = tmp;
var rollover = button.GetComponent<ButtonRolloverHandler>(); var rollover = button.GetComponent<ButtonRolloverHandler>();
tmp.GetComponent<TextTranslatorTMP>().Destroy(); tmp.GetComponent<TextTranslatorTMP>().Destroy();
button.gameObject.SetActive(true); button.gameObject.SetActive(true);
@@ -63,6 +68,7 @@ public class LocalEnumSetting : LocalSettingBase<int>
var highlight = button.transform.FindChild("ButtonHighlight")?.GetComponent<SpriteRenderer>(); var highlight = button.transform.FindChild("ButtonHighlight")?.GetComponent<SpriteRenderer>();
if (highlight != null) if (highlight != null)
{ {
_highlight = highlight;
highlight.color = Tab!.TabAppearance.EnumHoverColor; highlight.color = Tab!.TabAppearance.EnumHoverColor;
highlight.gameObject.SetActive(false); highlight.gameObject.SetActive(false);
} }
@@ -125,9 +131,19 @@ public class LocalEnumSetting : LocalSettingBase<int>
return button.gameObject; return button.gameObject;
} }
/// <inheritdoc/>
public override void RefreshOption()
{
_btnText.text = GetValueText();
if (_highlight)
{
_highlight.gameObject.SetActive(false);
}
}
/// <inheritdoc/> /// <inheritdoc/>
protected override string GetValueText() protected override string GetValueText()
{ {
return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name.Translate()}: <b>{Values[GetValue()]}</font></b>"; return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name.Translate()}: <b>{Values[GetValue()].Translate()}</font></b>";
} }
} }
@@ -35,6 +35,10 @@ public class LocalNumberSetting : LocalSettingBase<float>
/// </summary> /// </summary>
public MiraNumberSuffixes SuffixType { get; } public MiraNumberSuffixes SuffixType { get; }
private SpriteRenderer _highlight { get; set; }
private TextMeshPro _btnText { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LocalNumberSetting"/> class. /// Initializes a new instance of the <see cref="LocalNumberSetting"/> class.
/// </summary> /// </summary>
@@ -65,6 +69,7 @@ public class LocalNumberSetting : LocalSettingBase<float>
{ {
var button = Object.Instantiate(toggle, parent).GetComponent<PassiveButton>(); var button = Object.Instantiate(toggle, parent).GetComponent<PassiveButton>();
var tmp = button.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>(); var tmp = button.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>();
_btnText = tmp;
var rollover = button.GetComponent<ButtonRolloverHandler>(); var rollover = button.GetComponent<ButtonRolloverHandler>();
tmp.GetComponent<TextTranslatorTMP>().Destroy(); tmp.GetComponent<TextTranslatorTMP>().Destroy();
button.gameObject.SetActive(true); button.gameObject.SetActive(true);
@@ -74,6 +79,7 @@ public class LocalNumberSetting : LocalSettingBase<float>
var highlight = button.transform.FindChild("ButtonHighlight")?.GetComponent<SpriteRenderer>(); var highlight = button.transform.FindChild("ButtonHighlight")?.GetComponent<SpriteRenderer>();
if (highlight != null) if (highlight != null)
{ {
_highlight = highlight;
highlight.color = Tab!.TabAppearance.NumberHoverColor; highlight.color = Tab!.TabAppearance.NumberHoverColor;
highlight.gameObject.SetActive(false); highlight.gameObject.SetActive(false);
} }
@@ -136,6 +142,16 @@ public class LocalNumberSetting : LocalSettingBase<float>
return button.gameObject; return button.gameObject;
} }
/// <inheritdoc/>
public override void RefreshOption()
{
_btnText.text = GetValueText();
if (_highlight)
{
_highlight.gameObject.SetActive(false);
}
}
/// <inheritdoc/> /// <inheritdoc/>
protected override string GetValueText() protected override string GetValueText()
{ {
@@ -54,6 +54,9 @@ public abstract class LocalSettingBase<T> : ILocalSetting
/// <returns>The created setting.</returns> /// <returns>The created setting.</returns>
public abstract GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last); public abstract GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last);
/// <inheritdoc/>
public abstract void RefreshOption();
/// <summary> /// <summary>
/// Returns the formated string to use in the text of the setting. /// Returns the formated string to use in the text of the setting.
/// </summary> /// </summary>
@@ -40,6 +40,8 @@ public class LocalSliderSetting : LocalSettingBase<float>
/// </summary> /// </summary>
public MiraNumberSuffixes SuffixType { get; } public MiraNumberSuffixes SuffixType { get; }
private SlideBar _slider { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LocalSliderSetting"/> class. /// Initializes a new instance of the <see cref="LocalSliderSetting"/> class.
/// </summary> /// </summary>
@@ -71,6 +73,7 @@ public class LocalSliderSetting : LocalSettingBase<float>
public override GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last) public override GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last)
{ {
var newSlider = Object.Instantiate(slider, parent).GetComponent<SlideBar>(); var newSlider = Object.Instantiate(slider, parent).GetComponent<SlideBar>();
_slider = newSlider;
var rollover = newSlider.GetComponent<ButtonRolloverHandler>(); var rollover = newSlider.GetComponent<ButtonRolloverHandler>();
newSlider.Title = newSlider.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>(); // Why the hell slider has a title property that is not even assigned??? newSlider.Title = newSlider.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>(); // Why the hell slider has a title property that is not even assigned???
newSlider.Title.GetComponent<TextTranslatorTMP>().Destroy(); newSlider.Title.GetComponent<TextTranslatorTMP>().Destroy();
@@ -105,6 +108,12 @@ public class LocalSliderSetting : LocalSettingBase<float>
return newSlider.gameObject; return newSlider.gameObject;
} }
/// <inheritdoc/>
public override void RefreshOption()
{
_slider.Title.text = GetValueText();
}
/// <inheritdoc/> /// <inheritdoc/>
protected override string GetValueText() protected override string GetValueText()
{ {
@@ -15,6 +15,8 @@ namespace MiraAPI.LocalSettings.SettingTypes;
/// </summary> /// </summary>
public class LocalToggleSetting : LocalSettingBase<bool> public class LocalToggleSetting : LocalSettingBase<bool>
{ {
private ToggleButtonBehaviour _toggle { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LocalToggleSetting"/> class. /// Initializes a new instance of the <see cref="LocalToggleSetting"/> class.
/// </summary> /// </summary>
@@ -32,6 +34,7 @@ public class LocalToggleSetting : LocalSettingBase<bool>
public override GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last) public override GameObject CreateOption(ToggleButtonBehaviour toggle, SlideBar slider, Transform parent, ref float offset, ref int order, bool last)
{ {
var toggleObject = Object.Instantiate(toggle, parent).GetComponent<ToggleButtonBehaviour>(); var toggleObject = Object.Instantiate(toggle, parent).GetComponent<ToggleButtonBehaviour>();
_toggle = toggleObject;
var tmp = toggleObject.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>(); var tmp = toggleObject.transform.FindChild("Text_TMP").GetComponent<TextMeshPro>();
var passiveButton = toggleObject.GetComponent<PassiveButton>(); var passiveButton = toggleObject.GetComponent<PassiveButton>();
var rollover = toggleObject.GetComponent<ButtonRolloverHandler>(); var rollover = toggleObject.GetComponent<ButtonRolloverHandler>();
@@ -86,4 +89,11 @@ public class LocalToggleSetting : LocalSettingBase<bool>
return toggleObject.gameObject; return toggleObject.gameObject;
} }
/// <inheritdoc/>
public override void RefreshOption()
{
_toggle.UpdateText(GetValue());
_toggle.Background.color = GetValue() ? Tab!.TabAppearance.ToggleActiveColor : Tab!.TabAppearance.ToggleInactiveColor;
}
} }
@@ -213,6 +213,8 @@ public static class OptionsMenuPatches
if (background != null) if (background != null)
background.color = tab.TabAppearance.TabColor; background.color = tab.TabAppearance.TabColor;
tab.Open();
} }
private static void CustomClose(LocalSettingsTab tab) private static void CustomClose(LocalSettingsTab tab)
{ {
+29 -6
View File
@@ -4,6 +4,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Xml; using System.Xml;
using MiraAPI.Utilities;
using Reactor.Localization.Utilities; using Reactor.Localization.Utilities;
using UnityEngine; using UnityEngine;
@@ -97,7 +98,22 @@ public static class MiraLocaleManager
var callingAssembly = Assembly.GetCallingAssembly(); var callingAssembly = Assembly.GetCallingAssembly();
var dir = GetModLangDir(modGuid); var dir = GetModLangDir(modGuid);
LoadInternalStrings(callingAssembly, modGuid, dir); LoadInternalStrings(callingAssembly, callingAssembly.GetName().Name!, modGuid, dir);
}
/// <summary>
/// Registers translations for a mod. Copies embedded XML to mira_languages/{modGuid}/ on first run,
/// then loads all XML files from that directory into memory.
/// Call once per mod during plugin Load().
/// </summary>
/// <param name="modGuid">The mod GUID (e.g., "mira.example").</param>
/// <param name="internalName">The mod's root namespace (e.g., "mira.api").</param>
public static void Register(string modGuid, string internalName)
{
var callingAssembly = Assembly.GetCallingAssembly();
var dir = GetModLangDir(modGuid);
LoadInternalStrings(callingAssembly, internalName, modGuid, dir);
} }
/// <summary> /// <summary>
@@ -194,7 +210,7 @@ public static class MiraLocaleManager
return newString; return newString;
} }
private static void LoadInternalStrings(Assembly assembly, string modGuid, string dir) private static void LoadInternalStrings(Assembly assembly, string internalName, string modGuid, string dir)
{ {
Directory.CreateDirectory(dir); Directory.CreateDirectory(dir);
@@ -203,8 +219,9 @@ public static class MiraLocaleManager
Locale[modGuid] = []; Locale[modGuid] = [];
} }
var resourcePrefix = $"{assembly.GetName().Name}.Resources.Locale."; var resourcePrefix = $"{internalName}.Resources.Locale.";
var atLeastOneLoaded = false;
foreach (var locale in LangList) foreach (var locale in LangList)
{ {
using var resourceStream = using var resourceStream =
@@ -221,7 +238,8 @@ public static class MiraLocaleManager
{ {
var dict = ParseXmlFile(xmlContent); var dict = ParseXmlFile(xmlContent);
Locale[modGuid][locale.Key] = dict; Locale[modGuid][locale.Key] = dict;
Info($"Loaded {locale.Key} translation for mod {modGuid} ({dict.Count} keys)"); Info($"Loaded {locale.Key.ToDisplayString()} translation for mod {modGuid} ({dict.Count} keys)");
atLeastOneLoaded = true;
} }
catch (Exception e) catch (Exception e)
{ {
@@ -229,6 +247,11 @@ public static class MiraLocaleManager
} }
} }
if (!atLeastOneLoaded)
{
Error($"No internal strings were found for {internalName}!");
}
if (!Locale.TryGetValue(modGuid, out _)) if (!Locale.TryGetValue(modGuid, out _))
{ {
Locale[modGuid] = []; Locale[modGuid] = [];
@@ -247,11 +270,11 @@ public static class MiraLocaleManager
{ {
var dict = ParseXmlFile(filePath); var dict = ParseXmlFile(filePath);
Locale[modGuid][lang] = dict; Locale[modGuid][lang] = dict;
Info($"Loaded {lang} translation for mod {modGuid} ({dict.Count} keys)"); Info($"Loaded external {lang.ToDisplayString()} translation for mod {modGuid} ({dict.Count} keys)");
} }
catch (Exception e) catch (Exception e)
{ {
Error($"Failed to load translation {filePath}: {e.Message}"); Error($"Failed to load external translation {filePath}: {e.Message}");
} }
} }
} }