Add Translation API

This commit is contained in:
linmeideli committed 2026-08-14 13:05:30 +08:00
1 parent 535967475d
commit 0b81a8808a
67 files changed
+628 -145

No files matched your search

@@ -13,7 +13,7 @@ namespace MiraAPI.Example.Buttons.Freezer;
public class FreezeButton : CustomActionButton<PlayerControl> public class FreezeButton : CustomActionButton<PlayerControl>
{ {
public override string Name => "Freeze"; public override string Name => "button.freeze.name";
public override float Cooldown => OptionGroupSingleton<FreezerRoleSettings>.Instance.FreezeDuration; public override float Cooldown => OptionGroupSingleton<FreezerRoleSettings>.Instance.FreezeDuration;
public override bool PauseTimerInVent => true; public override bool PauseTimerInVent => true;
+1 -1
View File
@@ -9,7 +9,7 @@ namespace MiraAPI.Example.Buttons;
public class MeetingButton : CustomActionButton public class MeetingButton : CustomActionButton
{ {
public override string Name => "Call Meeting"; public override string Name => "button.callMeeting.name";
public override float Cooldown => 15; public override float Cooldown => 15;
@@ -10,7 +10,7 @@ namespace MiraAPI.Example.Buttons;
public class NeutralKillerButton : CustomActionButton public class NeutralKillerButton : CustomActionButton
{ {
public override string Name => "Win Game"; public override string Name => "button.winGame.name";
public override float Cooldown => 0f; public override float Cooldown => 0f;
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton; public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
public override MiraKeybind Keybind => ExampleKeybinds.NeutralWinKeybind; public override MiraKeybind Keybind => ExampleKeybinds.NeutralWinKeybind;
@@ -12,7 +12,7 @@ namespace MiraAPI.Example.Buttons.Teleporter;
public class TeleportButton : CustomActionButton public class TeleportButton : CustomActionButton
{ {
public override string Name => "Teleport"; public override string Name => "button.teleport.name";
public override float Cooldown => OptionGroupSingleton<TeleporterOptions>.Instance.TeleportCooldown.Value; public override float Cooldown => OptionGroupSingleton<TeleporterOptions>.Instance.TeleportCooldown.Value;
+4 -4
View File
@@ -1,4 +1,5 @@
using MiraAPI.Hud; using MiraAPI.Hud;
using MiraAPI.Translation;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using UnityEngine; using UnityEngine;
@@ -6,7 +7,7 @@ namespace MiraAPI.Example.Buttons;
public class ThinkButton : CustomActionButton public class ThinkButton : CustomActionButton
{ {
public override string Name => "Think"; public override string Name => "button.think.name";
public override float Cooldown => 15f; public override float Cooldown => 15f;
public override float EffectDuration => 10f; public override float EffectDuration => 10f;
public override int MaxUses => 1; public override int MaxUses => 1;
@@ -14,13 +15,12 @@ public class ThinkButton : CustomActionButton
public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton; public override LoadableAsset<Sprite> Sprite => ExampleAssets.ExampleButton;
protected override void OnClick() protected override void OnClick()
{ {
// This button does absolutely nothing besides changing its text Button!.OverrideText("button.think.thinking".Translate());
Button!.OverrideText("Thinking...");
} }
public override void OnEffectEnd() public override void OnEffectEnd()
{ {
Button!.OverrideText("Think"); Button!.OverrideText("button.think.name".Translate());
} }
public override bool IsEffectCancellable() public override bool IsEffectCancellable()
+3 -3
View File
@@ -6,12 +6,12 @@ namespace MiraAPI.Example;
[RegisterCustomColors] [RegisterCustomColors]
public static class ExampleColors public static class ExampleColors
{ {
public static CustomColor Cerulean { get; } = new("Cerulean", new Color(0.0f, 0.48f, 0.65f)) public static CustomColor Cerulean { get; } = new("color.cerulean", new Color(0.0f, 0.48f, 0.65f))
{ {
ColorBrightness = CustomColorBrightness.Lighter, ColorBrightness = CustomColorBrightness.Lighter,
}; };
public static CustomColor Rose { get; } = new("Rose", new Color(0.98f, 0.26f, 0.62f)); public static CustomColor Rose { get; } = new("color.rose", new Color(0.98f, 0.26f, 0.62f));
public static CustomColor Gold { get; } = new("Gold", new Color(1.0f, 0.84f, 0.0f)); public static CustomColor Gold { get; } = new("color.gold", new Color(1.0f, 0.84f, 0.0f));
} }
+2 -2
View File
@@ -8,7 +8,7 @@ namespace MiraAPI.Example;
public class ExampleLocalSettings(ConfigFile config) : LocalSettingsTab(config) public class ExampleLocalSettings(ConfigFile config) : LocalSettingsTab(config)
{ {
public override string TabName => "Mira Example"; public override string TabName => "settings.miraExample.name";
protected override bool ShouldCreateLabels => false; protected override bool ShouldCreateLabels => false;
public override LocalSettingTabAppearance TabAppearance => new() public override LocalSettingTabAppearance TabAppearance => new()
@@ -18,7 +18,7 @@ public class ExampleLocalSettings(ConfigFile config) : LocalSettingsTab(config)
}; };
[LocalSettingsButton] [LocalSettingsButton]
public LocalSettingsButton ExampleButton { get; private set; } = new("Example Button", OnExampleButtonClick); public LocalSettingsButton ExampleButton { get; private set; } = new("settings.miraExample.button.exampleButton", OnExampleButtonClick);
[LocalToggleSetting] [LocalToggleSetting]
public ConfigEntry<bool> ExampleToggle { get; private set; } = config.Bind("General", "Example Bool", true); public ConfigEntry<bool> ExampleToggle { get; private set; } = config.Bind("General", "Example Bool", true);
+5
View File
@@ -3,6 +3,7 @@ using BepInEx.Configuration;
using BepInEx.Unity.IL2CPP; using BepInEx.Unity.IL2CPP;
using HarmonyLib; using HarmonyLib;
using MiraAPI.PluginLoading; using MiraAPI.PluginLoading;
using MiraAPI.Translation;
using Reactor; using Reactor;
using Reactor.Networking; using Reactor.Networking;
using Reactor.Networking.Attributes; using Reactor.Networking.Attributes;
@@ -16,6 +17,10 @@ namespace MiraAPI.Example;
[ReactorModFlags(ModFlags.RequireOnAllClients)] [ReactorModFlags(ModFlags.RequireOnAllClients)]
public partial class ExamplePlugin : BasePlugin, IMiraPlugin public partial class ExamplePlugin : BasePlugin, IMiraPlugin
{ {
public ExamplePlugin()
{
TranslationManager.Register("mira.example");
}
public Harmony Harmony { get; } = new(Id); public Harmony Harmony { get; } = new(Id);
public string OptionsTitleText => "Mira API\nExample Mod"; public string OptionsTitleText => "Mira API\nExample Mod";
public ConfigFile GetConfigFile() => Config; public ConfigFile GetConfigFile() => Config;
@@ -1,5 +1,6 @@
using MiraAPI.Example.Roles; using MiraAPI.Example.Roles;
using MiraAPI.GameEnd; using MiraAPI.GameEnd;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using UnityEngine; using UnityEngine;
@@ -14,7 +15,7 @@ public class NeutralKillerGameOver : CustomGameOver
public override void AfterEndGameSetup(EndGameManager endGameManager) public override void AfterEndGameSetup(EndGameManager endGameManager)
{ {
endGameManager.WinText.text = "Outcast Killer Wins!"; endGameManager.WinText.text = "gameover.outcastKiller.win".Translate();
endGameManager.WinText.color = Color.magenta; endGameManager.WinText.color = Color.magenta;
endGameManager.BackgroundBar.material.SetColor(ShaderID.Color, Color.magenta); endGameManager.BackgroundBar.material.SetColor(ShaderID.Color, Color.magenta);
} }
+3 -2
View File
@@ -2,6 +2,7 @@
using MiraAPI.GameOptions; using MiraAPI.GameOptions;
using MiraAPI.Modifiers; using MiraAPI.Modifiers;
using MiraAPI.Modifiers.Types; using MiraAPI.Modifiers.Types;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using UnityEngine; using UnityEngine;
@@ -9,7 +10,7 @@ namespace MiraAPI.Example.Modifiers;
public class CaptainModifier : GameModifier public class CaptainModifier : GameModifier
{ {
public override string ModifierName => "Captain"; public override string ModifierName => "modifier.captain.name";
public override LoadableAsset<Sprite>? ModifierIcon => ExampleAssets.CallMeetingButton; public override LoadableAsset<Sprite>? ModifierIcon => ExampleAssets.CallMeetingButton;
public override void OnDeath(DeathReason reason) public override void OnDeath(DeathReason reason)
@@ -19,7 +20,7 @@ public class CaptainModifier : GameModifier
public override string GetDescription() public override string GetDescription()
{ {
return $"You can call a meeting from anywhere on the map."; return "modifier.captain.description";
} }
public override int GetAssignmentChance() public override int GetAssignmentChance()
@@ -7,7 +7,7 @@ namespace MiraAPI.Example.Modifiers.Freezer;
public class FreezeModifier : TimedModifier public class FreezeModifier : TimedModifier
{ {
public override string ModifierName => "Freezed"; public override string ModifierName => "modifier.freezed.name";
public override bool HideOnUi => false; public override bool HideOnUi => false;
public override float Duration => 15f; public override float Duration => 15f;
public override bool AutoStart => true; public override bool AutoStart => true;
@@ -4,11 +4,11 @@ namespace MiraAPI.Example.Modifiers;
public class HighPriorityModifier : GameModifier public class HighPriorityModifier : GameModifier
{ {
public override string ModifierName => "High Priority"; public override string ModifierName => "modifier.highPriority.name";
public override string GetDescription() public override string GetDescription()
{ {
return "You are high priority! Idrk what this means either tbh..."; return "modifier.highPriority.description";
} }
public override int GetAssignmentChance() public override int GetAssignmentChance()
+5 -5
View File
@@ -7,19 +7,19 @@ namespace MiraAPI.Example.Options;
public class ExampleOptions : AbstractOptionGroup public class ExampleOptions : AbstractOptionGroup
{ {
public override string GroupName => "Example Options 1"; public override string GroupName => "options.exampleOptions1.name";
public override Color GroupColor => Color.green; public override Color GroupColor => Color.green;
[ModdedToggleOption("Toggle Opt 1")] [ModdedToggleOption("options.exampleOptions1.toggleOpt1")]
public bool ToggleOpt { get; set; } = false; public bool ToggleOpt { get; set; } = false;
[ModdedToggleOption("Toggle Opt 2")] [ModdedToggleOption("options.exampleOptions1.toggleOpt1")]
public bool ToggleOpt2 { get; set; } = true; public bool ToggleOpt2 { get; set; } = true;
[ModdedNumberOption("Number Opt", min: 0, max: 10, increment: .25f, formatString: "0.00", suffixType: MiraNumberSuffixes.Percent)] [ModdedNumberOption("options.exampleOptions1.numberOpt", min: 0, max: 10, increment: .25f, formatString: "0.00", suffixType: MiraNumberSuffixes.Percent)]
public float NumberOpt { get; set; } = 4f; public float NumberOpt { get; set; } = 4f;
[ModdedEnumOption("Best API", typeof(BestApi), ["Mira API", "Mitochondria", "Reactor"])] [ModdedEnumOption("options.exampleOptions1.bestApi", typeof(BestApi), ["Mira API", "Mitochondria", "Reactor"])]
public BestApi Opt { get; set; } = BestApi.MiraAPI; public BestApi Opt { get; set; } = BestApi.MiraAPI;
} }
+6 -6
View File
@@ -6,22 +6,22 @@ namespace MiraAPI.Example.Options;
public class ExampleOptions2 : AbstractOptionGroup public class ExampleOptions2 : AbstractOptionGroup
{ {
public override string GroupName => "Example Options 2"; public override string GroupName => "options.exampleOptions2.name";
public override uint GroupPriority => 0; // This group will be displayed first. The default value is uint.MaxValue. public override uint GroupPriority => 0; // This group will be displayed first. The default value is uint.MaxValue.
public override MenuCategory ParentMenu => MenuCategory.CustomTwo; public override MenuCategory ParentMenu => MenuCategory.CustomTwo;
public ModdedPlayerOption PlayerOption2 { get; } = new("YOU MUST CHOSE A PLAYER", false); public ModdedPlayerOption PlayerOption2 { get; } = new("options.exampleOptions2.youMustChoseAPlayer", false);
public ModdedPlayerOption PlayerOption { get; } = new("Eh whatever"); public ModdedPlayerOption PlayerOption { get; } = new("options.exampleOptions2.ehWhatever");
public ModdedToggleOption ToggleOpt1 { get; } = new("Toggle Option 1", false); public ModdedToggleOption ToggleOpt1 { get; } = new("options.exampleOptions2.toggleOption1", false);
public ModdedToggleOption ToggleOpt2 { get; } = new("Toggle Option 2", false) public ModdedToggleOption ToggleOpt2 { get; } = new("options.exampleOptions2.toggleOption2", false)
{ {
Visible = () => OptionGroupSingleton<ExampleOptions2>.Instance.ToggleOpt1, // implicit cast from ModdedToggleOption to bool Visible = () => OptionGroupSingleton<ExampleOptions2>.Instance.ToggleOpt1, // implicit cast from ModdedToggleOption to bool
}; };
public ModdedEnumOption<TestingData> EnumOpt { get; } = new("Enum Opt", 0) public ModdedEnumOption<TestingData> EnumOpt { get; } = new("options.exampleOptions2.enumOpt", 0)
{ {
ChangedEvent = x => Logger<ExamplePlugin>.Info($"changed Enum Opt to {x}"), ChangedEvent = x => Logger<ExamplePlugin>.Info($"changed Enum Opt to {x}"),
}; };
@@ -6,14 +6,14 @@ namespace MiraAPI.Example.Options.Modifiers;
public class CaptainModifierSettings : AbstractOptionGroup<CaptainModifier> public class CaptainModifierSettings : AbstractOptionGroup<CaptainModifier>
{ {
public override string GroupName => "Captain Settings"; public override string GroupName => "options.captainSettings.name";
[ModdedNumberOption("Amount", 0, 5)] [ModdedNumberOption("options.captainSettings.amount", 0, 5)]
public float Amount { get; set; } = 1; public float Amount { get; set; } = 1;
[ModdedNumberOption("Chance", 0, 100, 10)] [ModdedNumberOption("options.captainSettings.chance", 0, 100, 10)]
public float Chance { get; set; } = 50; public float Chance { get; set; } = 50;
[ModdedNumberOption("Uses", 1, 5)] [ModdedNumberOption("options.captainSettings.uses", 1, 5)]
public float NumUses { get; set; } = 3; public float NumUses { get; set; } = 3;
} }
@@ -6,8 +6,8 @@ namespace MiraAPI.Example.Options.Modifiers;
public class FreezeModifierSettings : AbstractOptionGroup<FreezeModifier> public class FreezeModifierSettings : AbstractOptionGroup<FreezeModifier>
{ {
public override string GroupName => "Freeze"; public override string GroupName => "options.freezeModifierSettings.name";
[ModdedToggleOption("Use Color")] [ModdedToggleOption("options.freezeModifierSettings.useColor")]
public bool UseColor { get; set; } = true; public bool UseColor { get; set; } = true;
} }
@@ -5,15 +5,15 @@ namespace MiraAPI.Example.Options.Modifiers;
public class GeneralModifierOptions : AbstractOptionGroup public class GeneralModifierOptions : AbstractOptionGroup
{ {
public override string GroupName => "General"; public override string GroupName => "options.generalModifier.name";
public override uint GroupPriority => 0; public override uint GroupPriority => 0;
public override MenuCategory ParentMenu => MenuCategory.Modifiers; public override MenuCategory ParentMenu => MenuCategory.Modifiers;
[ModdedToggleOption("Some Boolean Option")] [ModdedToggleOption("options.generalModifier.someBooleanOption")]
public bool SomeBooleanOption { get; set; } = true; public bool SomeBooleanOption { get; set; } = true;
[ModdedNumberOption("Number Option", 0, 10, 1)] [ModdedNumberOption("options.generalModifier.numberOption", 0, 10, 1)]
public float NumberOption { get; set; } = 5; public float NumberOption { get; set; } = 5;
} }
@@ -13,11 +13,11 @@ public class FreezerRoleSettings : AbstractOptionGroup<FreezerRole>
TmpSpriteUtils.CreateSpriteAsset( TmpSpriteUtils.CreateSpriteAsset(
MiraAssets.ImpostorFile.LoadAsset(), MiraAssets.ImpostorFile.LoadAsset(),
"ApiExample.Role.Impostor.FreezerRole")); "ApiExample.Role.Impostor.FreezerRole"));
public override string GroupName => "Custom Role"; public override string GroupName => "options.freezerRoleSettings.name";
[ModdedNumberOption("Freeze Duration", 1, 15, 1, MiraNumberSuffixes.Seconds)] [ModdedNumberOption("options.freezerRoleSettings.freezeDuration", 1, 15, 1, MiraNumberSuffixes.Seconds)]
public float FreezeDuration { get; set; } = 5; public float FreezeDuration { get; set; } = 5;
[ModdedNumberOption("Freeze Uses", 1, 5)] [ModdedNumberOption("options.freezerRoleSettings.freezeUses", 1, 5)]
public float FreezeUses { get; set; } = 1; public float FreezeUses { get; set; } = 1;
} }
@@ -8,13 +8,13 @@ namespace MiraAPI.Example.Options.Roles;
public class TeleporterOptions : AbstractRoleOptionGroup<TeleporterRole> public class TeleporterOptions : AbstractRoleOptionGroup<TeleporterRole>
{ {
public override string GroupName => "Teleporter"; public override string GroupName => "options.teleporterOptions.name";
public ModdedNumberOption TeleportCooldown { get; set; } = new("Teleport Cooldown", 10, 5, 60, 2.5f, MiraNumberSuffixes.Seconds); public ModdedNumberOption TeleportCooldown { get; set; } = new("options.teleporterOptions.teleportCooldown", 10, 5, 60, 2.5f, MiraNumberSuffixes.Seconds);
[ModdedNumberOption("Teleport Duration", 5, 25, 1, MiraNumberSuffixes.Seconds)] [ModdedNumberOption("options.teleporterOptions.teleportDuration", 5, 25, 1, MiraNumberSuffixes.Seconds)]
public float TeleportDuration { get; set; } = 10; public float TeleportDuration { get; set; } = 10;
[ModdedNumberOption("Zoom Distance", 4, 15)] [ModdedNumberOption("options.teleporterOptions.zoomDistance", 4, 15)]
public float ZoomDistance { get; set; } = 6; public float ZoomDistance { get; set; } = 6;
} }
@@ -0,0 +1,66 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="teleporter.name">Teleporter</string>
<string name="teleporter.description">Zoom out and teleport across the map!</string>
<string name="freezer.name">Freezer</string>
<string name="freezer.description">Freeze another player for a duration of time.</string>
<string name="mayor.name">Mayor</string>
<string name="mayor.description">You get extra votes!</string>
<string name="chamelon.name">Chamelon</string>
<string name="chamelon.description">Stay invisible while not moving.</string>
<string name="outcastKiller.name">Outcast Killer</string>
<string name="outcastKiller.description">Outcast who can kill.</string>
<string name="outcast.name">Outcast</string>
<string name="outcast.roleTitle">OUTCAST</string>
<string name="outcast.teamIntroDescription">You are an Outcast. You do not have a team.</string>
<string name="button.think.name">Think</string>
<string name="button.think.thinking">Thinking...</string>
<string name="button.freeze.name">Freeze</string>
<string name="button.teleport.name">Teleport</string>
<string name="button.callMeeting.name">Call Meeting</string>
<string name="button.winGame.name">Win Game</string>
<string name="modifier.captain.name">Captain</string>
<string name="modifier.captain.description">You can call a meeting from anywhere on the map.</string>
<string name="modifier.freezed.name">Freezed</string>
<string name="modifier.highPriority.name">High Priority</string>
<string name="modifier.highPriority.description">You are high priority! Idrk what this means either tbh...</string>
<string name="options.exampleOptions1.name">Example Options 1</string>
<string name="options.exampleOptions1.toggleOpt1">Toggle Opt 1</string>
<string name="options.exampleOptions1.toggleOpt2">Toggle Opt 2</string>
<string name="options.exampleOptions1.numberOpt">Number Opt</string>
<string name="options.exampleOptions1.bestApi">Best API</string>
<string name="options.exampleOptions2.name">Example Options 2</string>
<string name="options.exampleOptions2.youMustChoseAPlayer">YOU MUST CHOSE A PLAYER</string>
<string name="options.exampleOptions2.ehWhatever">Eh whatever</string>
<string name="options.exampleOptions2.toggleOption1">Toggle Option 1</string>
<string name="options.exampleOptions2.toggleOption2">Toggle Option 2</string>
<string name="options.exampleOptions2.enumOpt">Enum Opt</string>
<string name="options.captainSettings.name">Captain Settings</string>
<string name="options.captainSettings.amount">Amount</string>
<string name="options.captainSettings.chance">Chance</string>
<string name="options.captainSettings.uses">Uses</string>
<string name="options.freezeModifierSettings.name">Freeze</string>
<string name="options.freezeModifierSettings.useColor">Use Color</string>
<string name="options.generalModifier.name">General</string>
<string name="options.generalModifier.someBooleanOption">Some Boolean Option</string>
<string name="options.generalModifier.numberOption">Number Option</string>
<string name="options.freezerRoleSettings.name">Custom Role</string>
<string name="options.freezerRoleSettings.freezeDuration">Freeze Duration</string>
<string name="options.freezerRoleSettings.freezeUses">Freeze Uses</string>
<string name="options.teleporterOptions.name">Teleporter</string>
<string name="options.teleporterOptions.teleportCooldown">Teleport Cooldown</string>
<string name="options.teleporterOptions.teleportDuration">Teleport Duration</string>
<string name="options.teleporterOptions.zoomDistance">Zoom Distance</string>
<string name="settings.miraExample.name">Mira Example</string>
<string name="settings.miraExample.exampleBool">Example Bool</string>
<string name="settings.miraExample.exampleBool2">Example Bool 2</string>
<string name="settings.miraExample.slider">Slider</string>
<string name="settings.miraExample.exampleNumber">Example Number</string>
<string name="settings.miraExample.exampleMultiplier">Example Multplier</string>
<string name="settings.miraExample.exampleEnum">Example Enum</string>
<string name="settings.miraExample.button.exampleButton">Example Button</string>
<string name="gameover.outcastKiller.win">Outcast Killer Wins!</string>
<string name="color.cerulean">Cerulean</string>
<string name="color.rose">Rose</string>
<string name="color.gold">Gold</string>
</resources>
@@ -0,0 +1,66 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="teleporter.name">传送者</string>
<string name="teleporter.description">缩小视野并在地图上传送!</string>
<string name="freezer.name">冰冻者</string>
<string name="freezer.description">将其他玩家冰冻一段时间。</string>
<string name="mayor.name">市长</string>
<string name="mayor.description">你拥有额外投票权!</string>
<string name="chamelon.name">变色龙</string>
<string name="chamelon.description">静止不动时保持隐身。</string>
<string name="outcastKiller.name">流放杀手</string>
<string name="outcastKiller.description">可以击杀的流放者。</string>
<string name="outcast.name">流放者</string>
<string name="outcast.roleTitle">流放者</string>
<string name="outcast.teamIntroDescription">你是流放者,你没有队伍。</string>
<string name="button.think.name">思考</string>
<string name="button.think.thinking">思考中...</string>
<string name="button.freeze.name">冰冻</string>
<string name="button.teleport.name">传送</string>
<string name="button.callMeeting.name">召集会议</string>
<string name="button.winGame.name">赢得游戏</string>
<string name="modifier.captain.name">队长</string>
<string name="modifier.captain.description">你可以在地图上任意位置召集会议。</string>
<string name="modifier.freezed.name">已被冰冻</string>
<string name="modifier.highPriority.name">高优先级</string>
<string name="modifier.highPriority.description">你是高优先级!老实说我也不太清楚这是什么意思……</string>
<string name="options.exampleOptions1.name">示例选项组 1</string>
<string name="options.exampleOptions1.toggleOpt1">开关选项 1</string>
<string name="options.exampleOptions1.toggleOpt2">开关选项 2</string>
<string name="options.exampleOptions1.numberOpt">数字选项</string>
<string name="options.exampleOptions1.bestApi">谁是最好的API</string>
<string name="options.exampleOptions2.name">示例选项组 2</string>
<string name="options.exampleOptions2.youMustChoseAPlayer">你必须选择一个玩家</string>
<string name="options.exampleOptions2.ehWhatever">咕咕嘎嘎</string>
<string name="options.exampleOptions2.toggleOption1">开关选项 1</string>
<string name="options.exampleOptions2.toggleOption2">开关选项 2</string>
<string name="options.exampleOptions2.enumOpt">枚举选项</string>
<string name="options.captainSettings.name">队长设置</string>
<string name="options.captainSettings.amount">数量</string>
<string name="options.captainSettings.chance">概率</string>
<string name="options.captainSettings.uses">使用次数</string>
<string name="options.freezeModifierSettings.name">冰冻</string>
<string name="options.freezeModifierSettings.useColor">使用颜色</string>
<string name="options.generalModifier.name">通用</string>
<string name="options.generalModifier.someBooleanOption">某个布尔选项</string>
<string name="options.generalModifier.numberOption">数字选项</string>
<string name="options.freezerRoleSettings.name">自定义职业</string>
<string name="options.freezerRoleSettings.freezeDuration">冰冻时长</string>
<string name="options.freezerRoleSettings.freezeUses">冰冻次数</string>
<string name="options.teleporterOptions.name">传送者</string>
<string name="options.teleporterOptions.teleportCooldown">传送冷却</string>
<string name="options.teleporterOptions.teleportDuration">传送持续时间</string>
<string name="options.teleporterOptions.zoomDistance">缩放距离</string>
<string name="settings.miraExample.name">Mira 示例插件</string>
<string name="settings.miraExample.exampleBool">示例布尔</string>
<string name="settings.miraExample.exampleBool2">示例布尔 2</string>
<string name="settings.miraExample.slider">滑块</string>
<string name="settings.miraExample.exampleNumber">示例数字</string>
<string name="settings.miraExample.exampleMultiplier">示例倍数</string>
<string name="settings.miraExample.exampleEnum">示例枚举</string>
<string name="settings.miraExample.button.exampleButton">示例按钮</string>
<string name="gameover.outcastKiller.win">流放杀手获胜!</string>
<string name="color.cerulean">天蓝</string>
<string name="color.rose">玫瑰红</string>
<string name="color.gold">金色</string>
</resources>
+2 -2
View File
@@ -10,8 +10,8 @@ namespace MiraAPI.Example.Roles;
public class ChameloenRole : CrewmateRole, ICustomRole public class ChameloenRole : CrewmateRole, ICustomRole
{ {
public string RoleName => "Chamelon"; public string RoleName => "chamelon.name";
public string RoleLongDescription => "Stay invisible while not moving."; public string RoleLongDescription => "chamelon.description";
public string RoleDescription => RoleLongDescription; public string RoleDescription => RoleLongDescription;
public Color RoleColor => Palette.AcceptedGreen; public Color RoleColor => Palette.AcceptedGreen;
public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate; public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate;
+2 -2
View File
@@ -6,8 +6,8 @@ namespace MiraAPI.Example.Roles;
public class FreezerRole : ImpostorRole, ICustomRole public class FreezerRole : ImpostorRole, ICustomRole
{ {
public string RoleName => "Freezer"; public string RoleName => "freezer.name";
public string RoleLongDescription => "Freeze another player for a duration of time."; public string RoleLongDescription => "freezer.description";
public string RoleDescription => RoleLongDescription; public string RoleDescription => RoleLongDescription;
public Color RoleColor => Palette.Blue; public Color RoleColor => Palette.Blue;
public ModdedRoleTeams Team => ModdedRoleTeams.Impostor; public ModdedRoleTeams Team => ModdedRoleTeams.Impostor;
+2 -2
View File
@@ -5,8 +5,8 @@ namespace MiraAPI.Example.Roles;
public class MayorRole : CrewmateRole, ICustomRole public class MayorRole : CrewmateRole, ICustomRole
{ {
public string RoleName => "Mayor"; public string RoleName => "mayor.name";
public string RoleLongDescription => "You get extra votes!"; public string RoleLongDescription => "mayor.description";
public string RoleDescription => RoleLongDescription; public string RoleDescription => RoleLongDescription;
public Color RoleColor => new Color32(221, 176, 152, 255); public Color RoleColor => new Color32(221, 176, 152, 255);
public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate; public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate;
+5 -5
View File
@@ -8,8 +8,8 @@ namespace MiraAPI.Example.Roles;
public class NeutralKillerRole : ImpostorRole, ICustomRole public class NeutralKillerRole : ImpostorRole, ICustomRole
{ {
public string RoleName => "Outcast Killer"; public string RoleName => "outcastKiller.name";
public string RoleDescription => "Outcast who can kill."; public string RoleDescription => "outcastKiller.description";
public string RoleLongDescription => RoleDescription; public string RoleLongDescription => RoleDescription;
public Color RoleColor => Color.magenta; public Color RoleColor => Color.magenta;
public ModdedRoleTeams Team => ModdedRoleTeams.Custom; public ModdedRoleTeams Team => ModdedRoleTeams.Custom;
@@ -21,13 +21,13 @@ public class NeutralKillerRole : ImpostorRole, ICustomRole
CanUseVent = true, CanUseVent = true,
}; };
public RoleOptionsGroup RoleOptionsGroup { get; } = new("Outcast", Color.gray); public RoleOptionsGroup RoleOptionsGroup { get; } = new("outcast.name", Color.gray);
[HideFromIl2Cpp] [HideFromIl2Cpp]
public TeamIntroConfiguration? IntroConfiguration { get; } = new( public TeamIntroConfiguration? IntroConfiguration { get; } = new(
Color.gray, Color.gray,
"OUTCAST", "outcast.roleTitle",
"You are an Outcast. You do not have a team."); "outcast.teamIntroDescription");
public override void SpawnTaskHeader(PlayerControl playerControl) public override void SpawnTaskHeader(PlayerControl playerControl)
{ {
+2 -2
View File
@@ -5,8 +5,8 @@ namespace MiraAPI.Example.Roles;
public class TeleporterRole : CrewmateRole, ICustomRole public class TeleporterRole : CrewmateRole, ICustomRole
{ {
public string RoleName => "Teleporter"; public string RoleName => "teleporter.name";
public string RoleLongDescription => "Zoom out and teleport across the map!"; public string RoleLongDescription => "teleporter.description";
public string RoleDescription => RoleLongDescription; public string RoleDescription => RoleLongDescription;
public Color RoleColor => new Color32(221, 176, 152, 255); public Color RoleColor => new Color32(221, 176, 152, 255);
public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate; public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate;
+5 -1
View File
@@ -1,6 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using HarmonyLib; using HarmonyLib;
using MiraAPI.Translation;
using Reactor.Localization.Utilities;
namespace MiraAPI.Colors; namespace MiraAPI.Colors;
@@ -20,7 +22,9 @@ public static class PaletteManager
{ {
var colors = CustomColors.Select(x => x.MainColor).ToArray(); var colors = CustomColors.Select(x => x.MainColor).ToArray();
var shadowColors = CustomColors.Select(x => x.ShadowColor).ToArray(); var shadowColors = CustomColors.Select(x => x.ShadowColor).ToArray();
var stringNames = CustomColors.Select(x => x.Name).ToArray(); var stringNames = CustomColors.Select(x =>
CustomStringName.CreateAndRegister(TranslationController.Instance.GetString(x.Name).Translate())
).ToArray();
Palette.PlayerColors = Palette.PlayerColors.ToArray().AddRangeToArray(colors); Palette.PlayerColors = Palette.PlayerColors.ToArray().AddRangeToArray(colors);
Palette.ShadowColors = Palette.ShadowColors.ToArray().AddRangeToArray(shadowColors); Palette.ShadowColors = Palette.ShadowColors.ToArray().AddRangeToArray(shadowColors);
+3 -1
View File
@@ -1,6 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using MiraAPI.Translation;
using Reactor.Utilities;
namespace MiraAPI.GameModes; namespace MiraAPI.GameModes;
@@ -61,7 +63,7 @@ public static class CustomGameModeManager
if (GameModes.Any(x => x.Key == gameMode.Id)) if (GameModes.Any(x => x.Key == gameMode.Id))
{ {
Error($"ID for gamemode {gameMode.Name} already exists!"); Error($"ID for gamemode {gameMode.Name.Translate()} already exists!");
return; return;
} }
+5 -3
View File
@@ -1,8 +1,10 @@
namespace MiraAPI.GameModes; using MiraAPI.Translation;
namespace MiraAPI.GameModes;
public class DefaultMode : CustomGameMode public class DefaultMode : CustomGameMode
{ {
public override string Name => "Default"; public override string Name => "gamemode.default.name";
public override string Description => "Default Among Us GameMode"; public override string Description => "gamemode.default.description".Translate();
public override int Id => 0; public override int Id => 0;
} }
@@ -1,6 +1,7 @@
using System; using System;
using System.Reflection; using System.Reflection;
using MiraAPI.GameOptions.OptionTypes; using MiraAPI.GameOptions.OptionTypes;
using MiraAPI.Translation;
namespace MiraAPI.GameOptions.Attributes; namespace MiraAPI.GameOptions.Attributes;
@@ -13,7 +14,7 @@ public class ModdedEnumOptionAttribute(string title, Type enumType, string[]? va
{ {
internal override IModdedOption CreateOption(object? value, PropertyInfo property) internal override IModdedOption CreateOption(object? value, PropertyInfo property)
{ {
var opt = new ModdedEnumOption(Title, (int)(value ?? 0), enumType, values); var opt = new ModdedEnumOption(Title.Translate(), (int)(value ?? 0), enumType, values);
return opt; return opt;
} }
@@ -1,6 +1,7 @@
using System; using System;
using System.Reflection; using System.Reflection;
using MiraAPI.GameOptions.OptionTypes; using MiraAPI.GameOptions.OptionTypes;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
namespace MiraAPI.GameOptions.Attributes; namespace MiraAPI.GameOptions.Attributes;
@@ -22,7 +23,7 @@ public class ModdedNumberOptionAttribute(
internal override IModdedOption CreateOption(object? value, PropertyInfo property) internal override IModdedOption CreateOption(object? value, PropertyInfo property)
{ {
return new ModdedNumberOption( return new ModdedNumberOption(
Title, Title.Translate(),
(float)(value ?? min + increment), (float)(value ?? min + increment),
min, min,
max, max,
@@ -1,6 +1,7 @@
using System; using System;
using System.Reflection; using System.Reflection;
using MiraAPI.GameOptions.OptionTypes; using MiraAPI.GameOptions.OptionTypes;
using MiraAPI.Translation;
namespace MiraAPI.GameOptions.Attributes; namespace MiraAPI.GameOptions.Attributes;
@@ -13,7 +14,7 @@ public class ModdedToggleOptionAttribute(string title) : ModdedOptionAttribute(t
{ {
internal override IModdedOption CreateOption(object? value, PropertyInfo property) internal override IModdedOption CreateOption(object? value, PropertyInfo property)
{ {
var toggleOpt = new ModdedToggleOption(Title, (bool)(value ?? false)); var toggleOpt = new ModdedToggleOption(Title.Translate(), (bool)(value ?? false));
return toggleOpt; return toggleOpt;
} }
@@ -2,6 +2,7 @@
using BepInEx.Configuration; using BepInEx.Configuration;
using MiraAPI.Networking; using MiraAPI.Networking;
using MiraAPI.PluginLoading; using MiraAPI.PluginLoading;
using MiraAPI.Translation;
using Reactor.Localization.Utilities; using Reactor.Localization.Utilities;
using Reactor.Networking.Rpc; using Reactor.Networking.Rpc;
using UnityEngine; using UnityEngine;
@@ -78,10 +79,10 @@ public abstract class ModdedOption<T> : IModdedOption
protected ModdedOption(string title, T defaultValue, bool includeInPreset = true) protected ModdedOption(string title, T defaultValue, bool includeInPreset = true)
{ {
Id = ModdedOptionsManager.NextId; Id = ModdedOptionsManager.NextId;
Title = title; Title = title.Translate();
DefaultValue = defaultValue; DefaultValue = defaultValue;
Value = defaultValue; Value = defaultValue;
StringName = CustomStringName.CreateAndRegister(Title); StringName = CustomStringName.CreateAndRegister(Title.Translate());
Visible = () => true; Visible = () => true;
IncludeInPreset = includeInPreset; IncludeInPreset = includeInPreset;
} }
@@ -2,6 +2,7 @@
using System.Linq; using System.Linq;
using Il2CppSystem.Collections.Generic; using Il2CppSystem.Collections.Generic;
using MiraAPI.Networking; using MiraAPI.Networking;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using UnityEngine; using UnityEngine;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
@@ -65,7 +66,7 @@ public class ModdedPlayerOption : ModdedOption<int>
playerOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged; playerOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
playerOption.Title = StringName; playerOption.Title = StringName;
playerOption.TitleText.text = TranslationController.Instance.GetString(StringName); playerOption.TitleText.text = Title.Translate();
playerOption.Values = Values; playerOption.Values = Values;
playerOption.Value = Value; playerOption.Value = Value;
playerOption.playerIndex = filteredList.FindIndex(p => p.PlayerId == Value); playerOption.playerIndex = filteredList.FindIndex(p => p.PlayerId == Value);
@@ -1,5 +1,6 @@
using System; using System;
using MiraAPI.Networking; using MiraAPI.Networking;
using MiraAPI.Translation;
using UnityEngine; using UnityEngine;
using Object = UnityEngine.Object; using Object = UnityEngine.Object;
@@ -35,7 +36,7 @@ public class ModdedToggleOption : ModdedOption<bool>
toggleOption.SetUpFromData(Data, 20); toggleOption.SetUpFromData(Data, 20);
toggleOption.Title = StringName; toggleOption.Title = StringName;
toggleOption.TitleText.text = Title; toggleOption.TitleText.text = Title.Translate();
toggleOption.CheckMark.enabled = Value; toggleOption.CheckMark.enabled = Value;
toggleOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged; toggleOption.OnValueChanged = (Il2CppSystem.Action<OptionBehaviour>)ValueChanged;
+2 -1
View File
@@ -5,6 +5,7 @@ using MiraAPI.Events.Mira;
using MiraAPI.Keybinds; using MiraAPI.Keybinds;
using MiraAPI.LocalSettings; using MiraAPI.LocalSettings;
using MiraAPI.Patches; using MiraAPI.Patches;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using Rewired; using Rewired;
@@ -150,7 +151,7 @@ public abstract class CustomActionButton
Button = Object.Instantiate(HudManager.Instance.AbilityButton, parent); Button = Object.Instantiate(HudManager.Instance.AbilityButton, parent);
Button.name = Name + "Button"; Button.name = Name + "Button";
Button.OverrideText(Name.ToUpperInvariant()); Button.OverrideText(Name.Translate().ToUpperInvariant());
Button.graphic.sprite = Sprite.LoadAsset(); Button.graphic.sprite = Sprite.LoadAsset();
+2 -1
View File
@@ -1,4 +1,5 @@
using System; using System;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using Reactor.Utilities.Extensions; using Reactor.Utilities.Extensions;
using TMPro; using TMPro;
@@ -51,7 +52,7 @@ public class LocalSettingsButton(string text, Action onClick)
button.transform.localPosition = new Vector3(order == 1 ? -1.185f : 1.185f, 1.85f - offset, -7); button.transform.localPosition = new Vector3(order == 1 ? -1.185f : 1.185f, 1.85f - offset, -7);
} }
tmp.text = Text; tmp.text = Text.Translate();
button.name = Text; button.name = Text;
button.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); button.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent();
rollover.OutColor = Tab!.TabAppearance.ButtonColor; rollover.OutColor = Tab!.TabAppearance.ButtonColor;
+5 -4
View File
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using BepInEx.Configuration; using BepInEx.Configuration;
using HarmonyLib; using HarmonyLib;
using MiraAPI.Patches.LocalSettings; using MiraAPI.Patches.LocalSettings;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using Reactor.Utilities.Extensions; using Reactor.Utilities.Extensions;
using TMPro; using TMPro;
@@ -216,7 +217,7 @@ public abstract class LocalSettingsTab(ConfigFile config)
tabButtonText.transform.localPosition = new Vector3(0.078f, 0, 0); tabButtonText.transform.localPosition = new Vector3(0.078f, 0, 0);
tabButtonText.transform.localScale = new Vector3(0.9f, 0.9f); tabButtonText.transform.localScale = new Vector3(0.9f, 0.9f);
tabButtonText.alignment = TextAlignmentOptions.Right; tabButtonText.alignment = TextAlignmentOptions.Right;
tabButtonText.text = $"<b>{GetShortName(TabName)}</b>"; tabButtonText.text = $"<b>{GetShortName(TabName.Translate())}</b>";
var tabButton = tabButtonObject.GetComponent<PassiveButton>(); var tabButton = tabButtonObject.GetComponent<PassiveButton>();
var rollover = tabButtonObject.Rollover; var rollover = tabButtonObject.Rollover;
@@ -244,7 +245,7 @@ public abstract class LocalSettingsTab(ConfigFile config)
tabButtonText.gameObject.SetActive(true); tabButtonText.gameObject.SetActive(true);
tabButtonText.transform.localPosition = new Vector3(-0.024f, 0, 0); tabButtonText.transform.localPosition = new Vector3(-0.024f, 0, 0);
tabButtonText.maxVisibleCharacters = int.MaxValue; tabButtonText.maxVisibleCharacters = int.MaxValue;
tabButtonText.text = TabName; tabButtonText.text = TabName.Translate();
tabButtonText.alignment = TextAlignmentOptions.Left; tabButtonText.alignment = TextAlignmentOptions.Left;
tabButtonRend?.gameObject.SetActive(!TabAppearance.HideIconOnHover); tabButtonRend?.gameObject.SetActive(!TabAppearance.HideIconOnHover);
})); }));
@@ -266,7 +267,7 @@ public abstract class LocalSettingsTab(ConfigFile config)
tabButtonText.transform.localPosition = new Vector3(0.1f, 0, 0); tabButtonText.transform.localPosition = new Vector3(0.1f, 0, 0);
tabButtonText.maxVisibleCharacters = 4; tabButtonText.maxVisibleCharacters = 4;
tabButtonText.text = $"<b>{GetShortName(TabName)}</b>"; tabButtonText.text = $"<b>{GetShortName(TabName.Translate())}</b>";
})); }));
return tabButtonObject.gameObject; return tabButtonObject.gameObject;
@@ -280,7 +281,7 @@ public abstract class LocalSettingsTab(ConfigFile config)
label.name = text; label.name = text;
var tmp = label.GetComponent<TextMeshPro>(); var tmp = label.GetComponent<TextMeshPro>();
tmp.text = $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\"><b>{text}</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;
var meshRend = label.GetComponent<MeshRenderer>(); var meshRend = label.GetComponent<MeshRenderer>();
@@ -1,6 +1,7 @@
using System; using System;
using System.Linq; using System.Linq;
using BepInEx.Configuration; using BepInEx.Configuration;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using Reactor.Utilities.Extensions; using Reactor.Utilities.Extensions;
using TMPro; using TMPro;
@@ -127,6 +128,6 @@ public class LocalEnumSetting : LocalSettingBase<int>
/// <inheritdoc/> /// <inheritdoc/>
protected override string GetValueText() protected override string GetValueText()
{ {
return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name}: <b>{Values[GetValue()]}</font></b>"; return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name.Translate()}: <b>{Values[GetValue()]}</font></b>";
} }
} }
@@ -1,5 +1,6 @@
using System; using System;
using BepInEx.Configuration; using BepInEx.Configuration;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using Reactor.Utilities.Extensions; using Reactor.Utilities.Extensions;
using TMPro; using TMPro;
@@ -140,6 +141,6 @@ public class LocalNumberSetting : LocalSettingBase<float>
{ {
var value = GetValue(); var value = GetValue();
var formated = Helpers.FormatValue(value, SuffixType, FormatString); var formated = Helpers.FormatValue(value, SuffixType, FormatString);
return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name}: <b>{formated}</font></b>"; return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name.Translate()}: <b>{formated}</font></b>";
} }
} }
@@ -1,5 +1,6 @@
using System; using System;
using BepInEx.Configuration; using BepInEx.Configuration;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using Reactor.Utilities.Extensions; using Reactor.Utilities.Extensions;
using TMPro; using TMPro;
@@ -112,9 +113,9 @@ public class LocalSliderSetting : LocalSettingBase<float>
var value = GetValue(); var value = GetValue();
var formated = Helpers.FormatValue(value, SuffixType, FormatString); var formated = Helpers.FormatValue(value, SuffixType, FormatString);
var maxFormated = Helpers.FormatValue(SliderRange.max, SuffixType, FormatString); var maxFormated = Helpers.FormatValue(SliderRange.max, SuffixType, FormatString);
return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name}: <b>{formated} / {maxFormated}</font></b>"; return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name.Translate()}: <b>{formated} / {maxFormated}</font></b>";
} }
return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name}</font></b>"; return $"<font=\"LiberationSans SDF\" material=\"LiberationSans SDF - Chat Message Masked\">{Name.Translate()}</font></b>";
} }
} }
@@ -1,5 +1,6 @@
using System; using System;
using BepInEx.Configuration; using BepInEx.Configuration;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using Reactor.Localization.Utilities; using Reactor.Localization.Utilities;
using TMPro; using TMPro;
@@ -46,9 +47,9 @@ public class LocalToggleSetting : LocalSettingBase<bool>
toggleObject.transform.localPosition = new Vector3(order == 1 ? -1.185f : 1.185f, 1.85f - offset, -7); toggleObject.transform.localPosition = new Vector3(order == 1 ? -1.185f : 1.185f, 1.85f - offset, -7);
} }
toggleObject.BaseText = CustomStringName.CreateAndRegister(Name); toggleObject.BaseText = CustomStringName.CreateAndRegister(Name.Translate());
toggleObject.UpdateText(GetValue()); toggleObject.UpdateText(GetValue());
toggleObject.name = Name; toggleObject.name = Name.Translate();
toggleObject.Background.color = GetValue() ? Tab!.TabAppearance.ToggleActiveColor : Tab!.TabAppearance.ToggleInactiveColor; toggleObject.Background.color = GetValue() ? Tab!.TabAppearance.ToggleActiveColor : Tab!.TabAppearance.ToggleInactiveColor;
passiveButton.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent(); passiveButton.OnClick = new UnityEngine.UI.Button.ButtonClickedEvent();
rollover.OverColor = Tab!.TabAppearance.ToggleHoverColor; rollover.OverColor = Tab!.TabAppearance.ToggleHoverColor;
+3
View File
@@ -4,6 +4,7 @@ using BepInEx;
using BepInEx.Unity.IL2CPP; using BepInEx.Unity.IL2CPP;
using HarmonyLib; using HarmonyLib;
using MiraAPI.PluginLoading; using MiraAPI.PluginLoading;
using MiraAPI.Translation;
using Reactor; using Reactor;
using Reactor.Networking; using Reactor.Networking;
using Reactor.Networking.Attributes; using Reactor.Networking.Attributes;
@@ -54,5 +55,7 @@ public partial class MiraApiPlugin : BasePlugin
PluginManager = new MiraPluginManager(); PluginManager = new MiraPluginManager();
PluginManager.Initialize(); PluginManager.Initialize();
TranslationManager.Register("mira.api");
} }
} }
+2 -1
View File
@@ -2,6 +2,7 @@
using MiraAPI.LocalSettings; using MiraAPI.LocalSettings;
using MiraAPI.LocalSettings.Attributes; using MiraAPI.LocalSettings.Attributes;
using MiraAPI.Patches; using MiraAPI.Patches;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using UnityEngine; using UnityEngine;
@@ -14,7 +15,7 @@ namespace MiraAPI;
public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config) public class MiraApiSettings(ConfigFile config) : LocalSettingsTab(config)
{ {
/// <inheritdoc /> /// <inheritdoc />
public override string TabName => "Mira API"; public override string TabName => "miraApi.name".Translate();
/// <inheritdoc /> /// <inheritdoc />
public override LocalSettingTabAppearance TabAppearance => new() public override LocalSettingTabAppearance TabAppearance => new()
+7 -5
View File
@@ -8,6 +8,8 @@ using MiraAPI.LocalSettings;
using MiraAPI.Modifiers.ModifierDisplay; using MiraAPI.Modifiers.ModifierDisplay;
using MiraAPI.Modifiers.Types; using MiraAPI.Modifiers.Types;
using MiraAPI.Patches.Roles; using MiraAPI.Patches.Roles;
using MiraAPI.Translation;
using Reactor.Utilities;
using Reactor.Utilities.Attributes; using Reactor.Utilities.Attributes;
using UnityEngine; using UnityEngine;
@@ -76,7 +78,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
} }
catch (Exception e) catch (Exception e)
{ {
Error($"Error while deactivating modifier {modifier.ModifierName}: {e.ToString()}"); Error($"Error while deactivating modifier {modifier.ModifierName.Translate()}: {e.ToString()}");
} }
Modifiers.Remove(modifier); Modifiers.Remove(modifier);
@@ -94,7 +96,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
} }
catch (Exception e) catch (Exception e)
{ {
Error($"Error while activating modifier {modifier.ModifierName}: {e.ToString()}"); Error($"Error while activating modifier {modifier.ModifierName.Translate()}: {e.ToString()}");
} }
if (modifier is TimedModifier { AutoStart: true } timer) if (modifier is TimedModifier { AutoStart: true } timer)
@@ -121,7 +123,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
} }
catch (Exception e) catch (Exception e)
{ {
Error($"Error while (fixed) updating modifier {modifier.ModifierName}: {e.ToString()}"); Error($"Error while (fixed) updating modifier {modifier.ModifierName.Translate()}: {e.ToString()}");
} }
} }
@@ -152,7 +154,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
} }
catch (Exception e) catch (Exception e)
{ {
Error($"Error while updating modifier {modifier.ModifierName}: {e.ToString()}"); Error($"Error while updating modifier {modifier.ModifierName.Translate()}: {e.ToString()}");
} }
} }
} }
@@ -351,7 +353,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
{ {
if (!ActiveModifiers.Contains(modifier)) if (!ActiveModifiers.Contains(modifier))
{ {
Error($"Cannot remove modifier {modifier.ModifierName} because it is not active on this player."); Error($"Cannot remove modifier {modifier.ModifierName.Translate()} because it is not active on this player.");
return; return;
} }
@@ -4,6 +4,7 @@ using System.Linq;
using HarmonyLib; using HarmonyLib;
using Il2CppInterop.Runtime.Attributes; using Il2CppInterop.Runtime.Attributes;
using MiraAPI.LocalSettings; using MiraAPI.LocalSettings;
using MiraAPI.Translation;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using Reactor.Utilities.Attributes; using Reactor.Utilities.Attributes;
using Reactor.Utilities.Extensions; using Reactor.Utilities.Extensions;
@@ -153,7 +154,7 @@ public class ModifierDisplayComponent(nint cppPtr) : MonoBehaviour(cppPtr)
private ModifierUiComponent CreateForModifier(BaseModifier modifier) private ModifierUiComponent CreateForModifier(BaseModifier modifier)
{ {
var newMod = Instantiate(_modTemplate, _children.transform); var newMod = Instantiate(_modTemplate, _children.transform);
newMod.transform.name = modifier.ModifierName; newMod.transform.name = modifier.ModifierName.Translate();
var comp = newMod.AddComponent<ModifierUiComponent>(); var comp = newMod.AddComponent<ModifierUiComponent>();
comp.Modifier = modifier; comp.Modifier = modifier;
return comp; return comp;
@@ -1,4 +1,5 @@
using Il2CppInterop.Runtime.Attributes; using Il2CppInterop.Runtime.Attributes;
using MiraAPI.Translation;
using Reactor.Utilities.Attributes; using Reactor.Utilities.Attributes;
using TMPro; using TMPro;
using UnityEngine; using UnityEngine;
@@ -50,8 +51,8 @@ public class ModifierUiComponent(nint cppPtr) : MonoBehaviour(cppPtr)
return; return;
} }
nameText.text = Modifier.ModifierName; nameText.text = Modifier.ModifierName.Translate();
desc.text = Modifier.GetDescription(); desc.text = Modifier.GetDescription().Translate();
modBg.gameObject.SetActive(true); modBg.gameObject.SetActive(true);
icon.gameObject.SetActive(Modifier.ModifierIcon != null); icon.gameObject.SetActive(Modifier.ModifierIcon != null);
+4 -3
View File
@@ -4,6 +4,7 @@ using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using MiraAPI.Modifiers.Types; using MiraAPI.Modifiers.Types;
using MiraAPI.PluginLoading; using MiraAPI.PluginLoading;
using MiraAPI.Translation;
using MiraAPI.Roles; using MiraAPI.Roles;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using Reactor.Utilities.Extensions; using Reactor.Utilities.Extensions;
@@ -138,7 +139,7 @@ public static class ModifierManager
{ {
if (MiraApiPlugin.IsDevBuild) if (MiraApiPlugin.IsDevBuild)
{ {
Warning($"No valid players for modifier {modifier.ModifierName}"); Warning($"No valid players for modifier {modifier.ModifierName.Translate()}");
} }
continue; continue;
} }
@@ -163,7 +164,7 @@ public static class ModifierManager
if (MiraApiPlugin.IsDevBuild) if (MiraApiPlugin.IsDevBuild)
{ {
Warning( Warning(
$"No available players for modifier {modifier.ModifierName} at assignment {i + 1}"); $"No available players for modifier {modifier.ModifierName.Translate()} at assignment {i + 1}");
} }
break; break;
@@ -174,7 +175,7 @@ public static class ModifierManager
{ {
if (MiraApiPlugin.IsDevBuild) if (MiraApiPlugin.IsDevBuild)
{ {
Warning($"Valid player for modifier {modifier.ModifierName} disappeared"); Warning($"Valid player for modifier {modifier.ModifierName.Translate()} disappeared");
} }
continue; continue;
@@ -7,7 +7,7 @@ using UnityEngine;
namespace MiraAPI.Patches; namespace MiraAPI.Patches;
[HarmonyPatch(typeof(DetectiveNotesMinigame), nameof(DetectiveNotesMinigame.SetImpostorPopup))] /*[HarmonyPatch(typeof(DetectiveNotesMinigame), nameof(DetectiveNotesMinigame.SetImpostorPopup))]
public static class DetectiveNotesMinigamePatch public static class DetectiveNotesMinigamePatch
{ {
public static void Prefix(DetectiveNotesMinigame __instance, bool active) public static void Prefix(DetectiveNotesMinigame __instance, bool active)
@@ -39,4 +39,4 @@ public static class DetectiveNotesMinigamePatch
} }
ControllerManager.Instance.OpenOverlayMenu(__instance.ImpostorOverlay, __instance.impostorTypePopup, __instance.ImpostorIconControllerSelectables[0], __instance.ImpostorIconControllerSelectables, false); ControllerManager.Instance.OpenOverlayMenu(__instance.ImpostorOverlay, __instance.impostorTypePopup, __instance.ImpostorIconControllerSelectables[0], __instance.ImpostorIconControllerSelectables, false);
} }
} }*/
+2 -1
View File
@@ -10,6 +10,7 @@ using MiraAPI.Modifiers;
using MiraAPI.Modifiers.Types; using MiraAPI.Modifiers.Types;
using MiraAPI.PluginLoading; using MiraAPI.PluginLoading;
using MiraAPI.Roles; using MiraAPI.Roles;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using Reactor.Utilities.Extensions; using Reactor.Utilities.Extensions;
@@ -458,7 +459,7 @@ public static class TaskAdderPatches
taskAddButton.role = null; taskAddButton.role = null;
taskAddButton.MyTask = null; taskAddButton.MyTask = null;
taskAddButton.SafePositionWorld = __instance.SafePositionWorld; taskAddButton.SafePositionWorld = __instance.SafePositionWorld;
taskAddButton.Text.text = modifier.ModifierName; taskAddButton.Text.text = modifier.ModifierName.Translate();
taskAddButton.Text.fontSizeMin = 1; taskAddButton.Text.fontSizeMin = 1;
taskAddButton.Text.EnableMasking(); taskAddButton.Text.EnableMasking();
taskAddButton.FileImage.color = modifier.FreeplayFileColor; taskAddButton.FileImage.color = modifier.FreeplayFileColor;
+3 -2
View File
@@ -4,6 +4,7 @@ using Il2CppInterop.Runtime.InteropTypes;
using MiraAPI.Events; using MiraAPI.Events;
using MiraAPI.Events.Vanilla.Gameplay; using MiraAPI.Events.Vanilla.Gameplay;
using MiraAPI.Roles; using MiraAPI.Roles;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
namespace MiraAPI.Patches; namespace MiraAPI.Patches;
@@ -78,8 +79,8 @@ public static class IntroCutscenePatches
{ {
__instance.BackgroundBar.material.SetColor(ShaderID.Color, introConfig.IntroTeamColor); __instance.BackgroundBar.material.SetColor(ShaderID.Color, introConfig.IntroTeamColor);
__instance.TeamTitle.color = introConfig.IntroTeamColor; __instance.TeamTitle.color = introConfig.IntroTeamColor;
__instance.TeamTitle.text = introConfig.IntroTeamTitle; __instance.TeamTitle.text = introConfig.IntroTeamTitle.Translate();
__instance.ImpostorText.text = introConfig.IntroTeamDescription; __instance.ImpostorText.text = introConfig.IntroTeamDescription.Translate();
} }
} }
@@ -6,6 +6,7 @@ using HarmonyLib;
using MiraAPI.GameOptions; using MiraAPI.GameOptions;
using MiraAPI.Modifiers; using MiraAPI.Modifiers;
using MiraAPI.Presets; using MiraAPI.Presets;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using Reactor.Localization.Utilities; using Reactor.Localization.Utilities;
@@ -257,7 +258,7 @@ internal static class GameOptionsMenuPatch
Quaternion.identity, Quaternion.identity,
container); container);
categoryHeaderMasked.SetHeader(CustomStringName.CreateAndRegister(group.GroupName), 20); categoryHeaderMasked.SetHeader(CustomStringName.CreateAndRegister(group.GroupName.Translate()), 20);
categoryHeaderMasked.Background.color = group.GroupColor; categoryHeaderMasked.Background.color = group.GroupColor;
categoryHeaderMasked.Divider.color = group.GroupColor; categoryHeaderMasked.Divider.color = group.GroupColor;
categoryHeaderMasked.Title.color = group.GroupColor.Equals(MiraApiPlugin.DefaultHeaderColor) categoryHeaderMasked.Title.color = group.GroupColor.Equals(MiraApiPlugin.DefaultHeaderColor)
@@ -276,7 +277,7 @@ internal static class GameOptionsMenuPatch
categoryHeaderMasked.gameObject.SetActive(false); categoryHeaderMasked.gameObject.SetActive(false);
var newText = Object.Instantiate(categoryHeaderMasked.Title, categoryHeaderMasked.transform); var newText = Object.Instantiate(categoryHeaderMasked.Title, categoryHeaderMasked.transform);
newText.text = "<size=70%>(Click to close)</size>"; newText.text = $"<size=70%>({"gamesetting.global.clicktoclose".Translate()})</size>";
newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f); newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f);
newText.gameObject.GetComponent<TextTranslatorTMP>().Destroy(); newText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
@@ -293,8 +294,8 @@ internal static class GameOptionsMenuPatch
{ {
group.AllOptionsHidden = !group.AllOptionsHidden; group.AllOptionsHidden = !group.AllOptionsHidden;
newText.text = group.AllOptionsHidden newText.text = group.AllOptionsHidden
? "<size=70%>(Click to open)</size>" ? $"<size=70%>({"gamesetting.global.clicktoopen".Translate()})</size>"
: "<size=70%>(Click to close)</size>"; : $"<size=70%>({"gamesetting.global.clicktoclose".Translate()})</size>";
})); }));
headerBtn.SetButtonEnableState(true); headerBtn.SetButtonEnableState(true);
@@ -7,6 +7,7 @@ using MiraAPI.GameOptions;
using MiraAPI.PluginLoading; using MiraAPI.PluginLoading;
using MiraAPI.Presets; using MiraAPI.Presets;
using MiraAPI.Roles; using MiraAPI.Roles;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using Reactor.Utilities.Extensions; using Reactor.Utilities.Extensions;
@@ -45,7 +46,7 @@ internal static class GamePresetsTabPatches
if (MenuState.Instance.CurrentModIdx != 0) if (MenuState.Instance.CurrentModIdx != 0)
{ {
__instance.PresetDescriptionText.text = "Select or create an options preset for this mod"; __instance.PresetDescriptionText.text = "gamesetting.preset.hint".Translate();
} }
else else
{ {
@@ -184,7 +185,7 @@ internal static class GamePresetsTabPatches
// set the button text and alignment // set the button text and alignment
var saveText = saveButton.buttonText; var saveText = saveButton.buttonText;
saveText.text = "Save"; saveText.text = "gamesetting.preset.save".Translate();
saveText.GetComponent<TextTranslatorTMP>().Destroy(); saveText.GetComponent<TextTranslatorTMP>().Destroy();
saveText.alignment = TextAlignmentOptions.Center; saveText.alignment = TextAlignmentOptions.Center;
saveText.transform.parent.localPosition = new Vector3( saveText.transform.parent.localPosition = new Vector3(
@@ -7,6 +7,7 @@ using MiraAPI.GameOptions;
using MiraAPI.Modifiers; using MiraAPI.Modifiers;
using MiraAPI.PluginLoading; using MiraAPI.PluginLoading;
using MiraAPI.Roles; using MiraAPI.Roles;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using Reactor.Localization.Utilities; using Reactor.Localization.Utilities;
@@ -45,7 +46,7 @@ public static class LobbyViewPanePatches
var pos = ModifiersTabButton.transform.localPosition; var pos = ModifiersTabButton.transform.localPosition;
pos.x = 2.1f; pos.x = 2.1f;
ModifiersTabButton.transform.localPosition = pos; ModifiersTabButton.transform.localPosition = pos;
ModifiersTabButton.buttonText.text = "Modifiers"; ModifiersTabButton.buttonText.text = "gamesetting.modifiers".Translate();
ModifiersTabButton.OnClick = new Button.ButtonClickedEvent(); ModifiersTabButton.OnClick = new Button.ButtonClickedEvent();
ModifiersTabButton.OnClick.AddListener( ModifiersTabButton.OnClick.AddListener(
(UnityAction)(() => (UnityAction)(() =>
@@ -300,7 +301,7 @@ public static class LobbyViewPanePatches
true); true);
categoryHeaderMasked.SetHeader(StringNames.Name, 61); categoryHeaderMasked.SetHeader(StringNames.Name, 61);
categoryHeaderMasked.Title.text = group.GroupName; categoryHeaderMasked.Title.text = group.GroupName.Translate();
categoryHeaderMasked.transform.localScale = Vector3.one; categoryHeaderMasked.transform.localScale = Vector3.one;
categoryHeaderMasked.transform.localPosition = new Vector3(-9.77f, num, -2f); categoryHeaderMasked.transform.localPosition = new Vector3(-9.77f, num, -2f);
menu.settingsInfo.Add(categoryHeaderMasked.gameObject); menu.settingsInfo.Add(categoryHeaderMasked.gameObject);
@@ -396,11 +397,12 @@ public static class LobbyViewPanePatches
var group = grouping.Key; var group = grouping.Key;
var name = group.Name switch var translatedName = group.Name.Translate();
var name = translatedName switch
{ {
"Crewmate" => StringNames.CrewmateRolesHeader, "Crewmate" => StringNames.CrewmateRolesHeader,
"Impostor" => StringNames.ImpostorRolesHeader, "Impostor" => StringNames.ImpostorRolesHeader,
_ => CustomStringName.CreateAndRegister(group.Name), _ => CustomStringName.CreateAndRegister(translatedName),
}; };
var categoryHeaderRoleVariant = Object.Instantiate(instance.categoryHeaderRoleOrigin, instance.settingsContainer, true); var categoryHeaderRoleVariant = Object.Instantiate(instance.categoryHeaderRoleOrigin, instance.settingsContainer, true);
+12 -11
View File
@@ -5,6 +5,7 @@ using MiraAPI.GameOptions;
using MiraAPI.Modifiers; using MiraAPI.Modifiers;
using MiraAPI.PluginLoading; using MiraAPI.PluginLoading;
using MiraAPI.Presets; using MiraAPI.Presets;
using MiraAPI.Translation;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using Reactor.Utilities.Attributes; using Reactor.Utilities.Attributes;
using Reactor.Utilities.Extensions; using Reactor.Utilities.Extensions;
@@ -144,7 +145,7 @@ public class MenuState(IntPtr cppPtr) : MonoBehaviour(cppPtr)
_largeButtons[MenuButton.Modifiers] = mBtn; _largeButtons[MenuButton.Modifiers] = mBtn;
mBtn.name = "ModifiersButton"; mBtn.name = "ModifiersButton";
mBtn.buttonText.gameObject.GetComponent<TextTranslatorTMP>().Destroy(); mBtn.buttonText.gameObject.GetComponent<TextTranslatorTMP>().Destroy();
mBtn.buttonText.text = "Modifiers"; mBtn.buttonText.text = "gamesetting.modifiers".Translate();
mBtn.OnClick = new Button.ButtonClickedEvent(); mBtn.OnClick = new Button.ButtonClickedEvent();
mBtn.OnClick.AddListener( mBtn.OnClick.AddListener(
(UnityAction)(() => (UnityAction)(() =>
@@ -213,7 +214,7 @@ public class MenuState(IntPtr cppPtr) : MonoBehaviour(cppPtr)
})); }));
var roleText = smRolesBtn.buttonText; var roleText = smRolesBtn.buttonText;
roleText.text = "Roles"; roleText.text = "gamesetting.roles".Translate();
roleText.GetComponent<TextTranslatorTMP>().Destroy(); roleText.GetComponent<TextTranslatorTMP>().Destroy();
roleText.alignment = TextAlignmentOptions.Center; roleText.alignment = TextAlignmentOptions.Center;
roleText.transform.parent.localPosition = new Vector3( roleText.transform.parent.localPosition = new Vector3(
@@ -238,7 +239,7 @@ public class MenuState(IntPtr cppPtr) : MonoBehaviour(cppPtr)
var smModBtn = Instantiate(smRolesBtn, smRolesBtn.transform.parent); var smModBtn = Instantiate(smRolesBtn, smRolesBtn.transform.parent);
_smallButtons[MenuButton.Modifiers] = smModBtn; _smallButtons[MenuButton.Modifiers] = smModBtn;
smModBtn.name = "SmallModifiersButton"; smModBtn.name = "SmallModifiersButton";
smModBtn.buttonText.text = "Modifiers"; smModBtn.buttonText.text = "gamesetting.modifiers".Translate();
smModBtn.OnClick = new Button.ButtonClickedEvent(); smModBtn.OnClick = new Button.ButtonClickedEvent();
smModBtn.OnClick.AddListener( smModBtn.OnClick.AddListener(
(UnityAction)(() => (UnityAction)(() =>
@@ -383,7 +384,7 @@ public class MenuState(IntPtr cppPtr) : MonoBehaviour(cppPtr)
} }
else else
{ {
var modName = CurrentMod.MiraPlugin.OptionsTitleText; var modName = CurrentMod.MiraPlugin.OptionsTitleText.Translate();
_text.text = $"<size=40%>(Page {CurrentModIdx + 1}/{ModCount + 1})</size>\n" + _text.text = $"<size=40%>(Page {CurrentModIdx + 1}/{ModCount + 1})</size>\n" +
modName[..Math.Min(modName.Length, 25)]; modName[..Math.Min(modName.Length, 25)];
} }
@@ -485,12 +486,12 @@ public class MenuState(IntPtr cppPtr) : MonoBehaviour(cppPtr)
_smallButtons[MenuButton.CustomOne].gameObject.SetActive(true); _smallButtons[MenuButton.CustomOne].gameObject.SetActive(true);
_smallButtons[MenuButton.CustomOne].transform.localPosition = _smallButtons[MenuButton.CustomOne].transform.localPosition =
new Vector3(leftPos, position.y, position.z); new Vector3(leftPos, position.y, position.z);
_smallButtons[MenuButton.CustomOne].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameOne; _smallButtons[MenuButton.CustomOne].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameOne.Translate();
_smallButtons[MenuButton.CustomTwo].gameObject.SetActive(true); _smallButtons[MenuButton.CustomTwo].gameObject.SetActive(true);
_smallButtons[MenuButton.CustomTwo].transform.localPosition = _smallButtons[MenuButton.CustomTwo].transform.localPosition =
new Vector3(rightPos, position.y, position.z); new Vector3(rightPos, position.y, position.z);
_smallButtons[MenuButton.CustomTwo].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameTwo; _smallButtons[MenuButton.CustomTwo].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameTwo.Translate();
position.y -= 0.637f; position.y -= 0.637f;
} }
@@ -499,7 +500,7 @@ public class MenuState(IntPtr cppPtr) : MonoBehaviour(cppPtr)
{ {
_largeButtons[MenuButton.CustomOne].gameObject.SetActive(true); _largeButtons[MenuButton.CustomOne].gameObject.SetActive(true);
_largeButtons[MenuButton.CustomOne].transform.localPosition = position; _largeButtons[MenuButton.CustomOne].transform.localPosition = position;
_largeButtons[MenuButton.CustomOne].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameOne; _largeButtons[MenuButton.CustomOne].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameOne.Translate();
position.y -= 0.637f; position.y -= 0.637f;
} }
else else
@@ -512,7 +513,7 @@ public class MenuState(IntPtr cppPtr) : MonoBehaviour(cppPtr)
{ {
_largeButtons[MenuButton.CustomTwo].gameObject.SetActive(true); _largeButtons[MenuButton.CustomTwo].gameObject.SetActive(true);
_largeButtons[MenuButton.CustomTwo].transform.localPosition = position; _largeButtons[MenuButton.CustomTwo].transform.localPosition = position;
_largeButtons[MenuButton.CustomTwo].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameTwo; _largeButtons[MenuButton.CustomTwo].buttonText.text = CurrentMod.MiraPlugin.CustomOptionMenuNameTwo.Translate();
position.y -= 0.637f; position.y -= 0.637f;
} }
else else
@@ -566,15 +567,15 @@ public class MenuState(IntPtr cppPtr) : MonoBehaviour(cppPtr)
case MenuCategory.Modifiers: case MenuCategory.Modifiers:
menu.GameSettingsTab.gameObject.SetActive(true); menu.GameSettingsTab.gameObject.SetActive(true);
menu.MenuDescriptionText.text = CurrentMod.MiraPlugin.ModifierMenuDescription; menu.MenuDescriptionText.text = CurrentMod.MiraPlugin.ModifierMenuDescription.Translate();
break; break;
case MenuCategory.CustomOne: case MenuCategory.CustomOne:
menu.GameSettingsTab.gameObject.SetActive(true); menu.GameSettingsTab.gameObject.SetActive(true);
menu.MenuDescriptionText.text = CurrentMod.MiraPlugin.CustomOptionMenuOneDescription; menu.MenuDescriptionText.text = CurrentMod.MiraPlugin.CustomOptionMenuOneDescription.Translate();
break; break;
case MenuCategory.CustomTwo: case MenuCategory.CustomTwo:
menu.GameSettingsTab.gameObject.SetActive(true); menu.GameSettingsTab.gameObject.SetActive(true);
menu.MenuDescriptionText.text = CurrentMod.MiraPlugin.CustomOptionMenuTwoDescription; menu.MenuDescriptionText.text = CurrentMod.MiraPlugin.CustomOptionMenuTwoDescription.Translate();
break; break;
} }
} }
+4 -3
View File
@@ -2,6 +2,7 @@
using Il2CppSystem; using Il2CppSystem;
using MiraAPI.GameOptions; using MiraAPI.GameOptions;
using MiraAPI.Roles; using MiraAPI.Roles;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using UnityEngine; using UnityEngine;
using Object = Il2CppSystem.Object; using Object = Il2CppSystem.Object;
@@ -162,7 +163,7 @@ public static class OptionsPatches
return true; return true;
} }
__instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>()); __instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>()).Translate();
return false; return false;
} }
@@ -200,7 +201,7 @@ public static class OptionsPatches
return true; return true;
} }
__instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>()); __instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>()).Translate();
return false; return false;
} }
@@ -307,7 +308,7 @@ public static class OptionsPatches
return true; return true;
} }
__instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>()); __instance.TitleText.text = TranslationController.Instance.GetString(__instance.Title, Array.Empty<Object>()).Translate();
__instance.ValueText.text = TranslationController.Instance.GetString(__instance.Values[__instance.Value], Array.Empty<Object>()); __instance.ValueText.text = TranslationController.Instance.GetString(__instance.Values[__instance.Value], Array.Empty<Object>());
return false; return false;
@@ -8,6 +8,7 @@ using HarmonyLib;
using MiraAPI.GameOptions; using MiraAPI.GameOptions;
using MiraAPI.Networking; using MiraAPI.Networking;
using MiraAPI.Roles; using MiraAPI.Roles;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using Reactor.Localization.Utilities; using Reactor.Localization.Utilities;
@@ -185,11 +186,12 @@ public static class RoleSettingMenuPatches
RoleGroupHidden.TryAdd(group, false); RoleGroupHidden.TryAdd(group, false);
var name = group.Name switch var translatedName = group.Name.Translate();
var name = translatedName switch
{ {
"Crewmate" => StringNames.CrewmateRolesHeader, "Crewmate" => StringNames.CrewmateRolesHeader,
"Impostor" => StringNames.ImpostorRolesHeader, "Impostor" => StringNames.ImpostorRolesHeader,
_ => CustomStringName.CreateAndRegister(group.Name), _ => CustomStringName.CreateAndRegister(translatedName),
}; };
var categoryHeaderMasked = Object.Instantiate( var categoryHeaderMasked = Object.Instantiate(
@@ -264,8 +266,8 @@ public static class RoleSettingMenuPatches
quotaInst.gameObject.SetActive(!RoleGroupHidden[group]); quotaInst.gameObject.SetActive(!RoleGroupHidden[group]);
var label = RoleGroupHidden[group] var label = RoleGroupHidden[group]
? "(Click to open)" ? $"({"gamesetting.global.clicktoopen".Translate()})"
: "(Click to close)"; : $"({"gamesetting.global.clicktoclose".Translate()})";
var newText = Object.Instantiate(categoryHeaderMasked.Title, categoryHeaderMasked.transform); var newText = Object.Instantiate(categoryHeaderMasked.Title, categoryHeaderMasked.transform);
newText.text = $"<size=70%>{label}</size>"; newText.text = $"<size=70%>{label}</size>";
newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f); newText.transform.localPosition = new Vector3(2.6249f, -0.165f, 0f);
@@ -524,7 +526,7 @@ public static class RoleSettingMenuPatches
return; return;
} }
__instance.roleDescriptionText.text = customRole.RoleLongDescription; __instance.roleDescriptionText.text = customRole.RoleLongDescription.Translate();
__instance.roleTitleText.text = role.GetRoleName(); __instance.roleTitleText.text = role.GetRoleName();
var imgBg = __instance.AdvancedRolesSettings.transform.FindChild("Imagebackground"); var imgBg = __instance.AdvancedRolesSettings.transform.FindChild("Imagebackground");
@@ -563,7 +565,7 @@ public static class RoleSettingMenuPatches
comp.Destroy(); comp.Destroy();
} }
categoryHeaderMasked.Title.text = "RETURN TO ROLE SETTINGS"; categoryHeaderMasked.Title.text = "miraApi.returnToRoleSettings".Translate();
if (!categoryHeaderMasked.gameObject.TryGetComponent<PassiveButton>(out _)) if (!categoryHeaderMasked.gameObject.TryGetComponent<PassiveButton>(out _))
{ {
+2 -1
View File
@@ -20,6 +20,7 @@ using MiraAPI.Modifiers;
using MiraAPI.Patches.Options; using MiraAPI.Patches.Options;
using MiraAPI.Presets; using MiraAPI.Presets;
using MiraAPI.Roles; using MiraAPI.Roles;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using Reactor.Networking; using Reactor.Networking;
using Reactor.Utilities; using Reactor.Utilities;
@@ -423,7 +424,7 @@ public sealed class MiraPluginManager
KeybindManager.Keybinds.Add(keybind); KeybindManager.Keybinds.Add(keybind);
if (source is IMiraPlugin miraPlugin) if (source is IMiraPlugin miraPlugin)
{ {
keybind.SourcePluginName = miraPlugin.OptionsTitleText; keybind.SourcePluginName = miraPlugin.OptionsTitleText.Translate();
} }
else if (source is MiraApiPlugin) else if (source is MiraApiPlugin)
{ {
@@ -0,0 +1,28 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="miraApi.name">Mira API</string>
<string name="miraApi.visualsUi">Visuals/UI</string>
<string name="miraApi.freeplay">Freeplay</string>
<string name="miraApi.accessibility">Accessibility</string>
<string name="miraApi.miraApiBrand">MiraAPI</string>
<string name="miraApi.showModifiersHudOnLeftSide">Show Modifiers HUD on Left Side</string>
<string name="miraApi.showKeybindsOnButtons">Show Keybinds on Buttons</string>
<string name="miraApi.buttonScaleFactor">Button Scale Factor</string>
<string name="miraApi.maxFps">Max FPS</string>
<string name="miraApi.prettyTaskLaptop">Pretty Task Laptop</string>
<string name="miraApi.enableSabotageFlashes">Enable Sabotage Flashes</string>
<string name="miraApi.enableSabotageBlare">Enable Sabotage Blare</string>
<string name="miraApi.returnToRoleSettings">RETURN TO ROLE SETTINGS</string>
<string name="miraApi.customCategory1">Custom Category 1</string>
<string name="miraApi.customCategory2">Custom Category 2</string>
<string name="miraApi.modifierMenuDescription">Configure modifiers and their settings here!</string>
<string name="miraApi.customOptionMenuDescription">Apply game settings for this mod!</string>
<string name="gamemode.default.name">Default</string>
<string name="gamemode.default.description">Default Among Us GameMode</string>
<string name="gamesetting.global.clicktoclose">Click to close</string>
<string name="gamesetting.global.clicktoopen">Click to open</string>
<string name="gamesetting.preset.hint">Select or create a options preset for this game</string>
<string name="gamesetting.preset.save">Save</string>
<string name="gamesetting.roles">Roles</string>
<string name="gamesetting.modifiers">Modifiers</string>
</resources>
@@ -0,0 +1,27 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="miraApi.name">Mira API</string>
<string name="miraApi.visualsUi">界面/视觉</string>
<string name="miraApi.freeplay">自由模式</string>
<string name="miraApi.accessibility">无障碍</string>
<string name="miraApi.miraApiBrand">MiraAPI</string>
<string name="miraApi.showModifiersHudOnLeftSide">在左侧显示附加职业UI</string>
<string name="miraApi.showKeybindsOnButtons">在按钮上显示快捷键</string>
<string name="miraApi.buttonScaleFactor">按钮缩放比例</string>
<string name="miraApi.maxFps">最大帧数</string>
<string name="miraApi.prettyTaskLaptop">美化任务笔记本</string>
<string name="miraApi.enableSabotageFlashes">启用破坏红色闪烁</string>
<string name="miraApi.enableSabotageBlare">启用破坏音效</string>
<string name="miraApi.returnToRoleSettings">返回职业设置</string>
<string name="miraApi.customCategory1">自定义类别 1</string>
<string name="miraApi.customCategory2">自定义类别 2</string>
<string name="miraApi.modifierMenuDescription">在此配置附加职业及其设置!</string>
<string name="miraApi.customOptionMenuDescription">为此模组应用游戏设置!</string>
<string name="gamemode.default.name">默认</string>
<string name="gamemode.default.description">默认原版游戏模式</string>
<string name="gamesetting.global.clicktoclose">点击关闭</string>
<string name="gamesetting.preset.hint">为本游戏选择或创建一个预设</string>
<string name="gamesetting.preset.save">保存</string>
<string name="gamesetting.roles">职业</string>
<string name="gamesetting.modifiers">附加职业</string>
</resources>
+7 -6
View File
@@ -7,6 +7,7 @@ using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppInterop.Runtime.InteropTypes.Arrays;
using MiraAPI.Networking; using MiraAPI.Networking;
using MiraAPI.PluginLoading; using MiraAPI.PluginLoading;
using MiraAPI.Translation;
using MiraAPI.Utilities; using MiraAPI.Utilities;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using Reactor.Localization.Utilities; using Reactor.Localization.Utilities;
@@ -131,9 +132,9 @@ public static class CustomRoleManager
roleBehaviour.Role = (RoleTypes)roleId; roleBehaviour.Role = (RoleTypes)roleId;
roleBehaviour.TeamType = customRole.Team == ModdedRoleTeams.Custom ? RoleTeamTypes.Crewmate : (RoleTeamTypes)customRole.Team; roleBehaviour.TeamType = customRole.Team == ModdedRoleTeams.Custom ? RoleTeamTypes.Crewmate : (RoleTeamTypes)customRole.Team;
roleBehaviour.NameColor = customRole.RoleColor; roleBehaviour.NameColor = customRole.RoleColor;
roleBehaviour.StringName = CustomStringName.CreateAndRegister(customRole.RoleName); roleBehaviour.StringName = CustomStringName.CreateAndRegister(customRole.RoleName.Translate());
roleBehaviour.BlurbName = CustomStringName.CreateAndRegister(customRole.RoleDescription); roleBehaviour.BlurbName = CustomStringName.CreateAndRegister(customRole.RoleDescription.Translate());
roleBehaviour.BlurbNameLong = CustomStringName.CreateAndRegister(customRole.RoleLongDescription); roleBehaviour.BlurbNameLong = CustomStringName.CreateAndRegister(customRole.RoleLongDescription.Translate());
roleBehaviour.AffectedByLightAffectors = customRole.Configuration.AffectedByLightOnAirship; roleBehaviour.AffectedByLightAffectors = customRole.Configuration.AffectedByLightOnAirship;
roleBehaviour.CanBeKilled = customRole.Configuration.CanGetKilled; roleBehaviour.CanBeKilled = customRole.Configuration.CanGetKilled;
roleBehaviour.CanUseKillButton = customRole.Configuration.UseVanillaKillButton; roleBehaviour.CanUseKillButton = customRole.Configuration.UseVanillaKillButton;
@@ -178,7 +179,7 @@ public static class CustomRoleManager
if (useTaskHint && !overridesTaskText) if (useTaskHint && !overridesTaskText)
{ {
Error($"Role {customRole.RoleName} is using RoleHintType.TaskHint but does not override SpawnTaskHeader!"); Error($"Role {customRole.RoleName.Translate()} is using RoleHintType.TaskHint but does not override SpawnTaskHeader!");
} }
CustomRoles.Add(roleId, roleBehaviour); CustomRoles.Add(roleId, roleBehaviour);
@@ -246,9 +247,9 @@ public static class CustomRoleManager
{ {
var tabText = panel.tab.gameObject.GetComponentInChildren<TextMeshPro>(); var tabText = panel.tab.gameObject.GetComponentInChildren<TextMeshPro>();
var ogPanel = HudManager.Instance.TaskStuff.transform.FindChild("TaskPanel").gameObject.GetComponent<TaskPanelBehaviour>(); var ogPanel = HudManager.Instance.TaskStuff.transform.FindChild("TaskPanel").gameObject.GetComponent<TaskPanelBehaviour>();
if (tabText.text != role.RoleName) if (tabText.text != role.RoleName.Translate())
{ {
tabText.text = role.RoleName; tabText.text = role.RoleName.Translate();
} }
var y = ogPanel.taskText.textBounds.size.y + 1; var y = ogPanel.taskText.textBounds.size.y + 1;
+3 -2
View File
@@ -4,6 +4,7 @@ using System.Globalization;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using AmongUs.GameOptions; using AmongUs.GameOptions;
using MiraAPI.Translation;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using UnityEngine; using UnityEngine;
@@ -101,9 +102,9 @@ public static class CustomRoleUtils
public static StringBuilder CreateForRole(ICustomRole role) public static StringBuilder CreateForRole(ICustomRole role)
{ {
var taskStringBuilder = new StringBuilder(); var taskStringBuilder = new StringBuilder();
taskStringBuilder.AppendLine(CultureInfo.InvariantCulture, $"{role.RoleColor.ToTextColor()}Your role is <b>{role.RoleName}.</b></color>"); taskStringBuilder.AppendLine(CultureInfo.InvariantCulture, $"{role.RoleColor.ToTextColor()}Your role is <b>{role.RoleName.Translate()}.</b></color>");
taskStringBuilder.Append("<size=70%>"); taskStringBuilder.Append("<size=70%>");
taskStringBuilder.AppendLine(CultureInfo.InvariantCulture, $"{role.RoleLongDescription}"); taskStringBuilder.AppendLine(CultureInfo.InvariantCulture, $"{role.RoleLongDescription.Translate()}");
return taskStringBuilder; return taskStringBuilder;
} }
+6 -5
View File
@@ -4,6 +4,7 @@ using BepInEx.Configuration;
using MiraAPI.GameOptions; using MiraAPI.GameOptions;
using MiraAPI.Modifiers; using MiraAPI.Modifiers;
using MiraAPI.PluginLoading; using MiraAPI.PluginLoading;
using MiraAPI.Translation;
using UnityEngine; using UnityEngine;
namespace MiraAPI.Roles; namespace MiraAPI.Roles;
@@ -56,7 +57,7 @@ public interface ICustomRole : IOptionable
ModdedRoleTeams.Crewmate => RoleOptionsGroup.Crewmate, ModdedRoleTeams.Crewmate => RoleOptionsGroup.Crewmate,
ModdedRoleTeams.Impostor => RoleOptionsGroup.Impostor, ModdedRoleTeams.Impostor => RoleOptionsGroup.Impostor,
ModdedRoleTeams.Custom => RoleOptionsGroup.Neutral, ModdedRoleTeams.Custom => RoleOptionsGroup.Neutral,
_ => new RoleOptionsGroup(RoleName, RoleColor), _ => new RoleOptionsGroup(RoleName.Translate(), RoleColor),
}; };
/// <summary> /// <summary>
@@ -156,7 +157,7 @@ public interface ICustomRole : IOptionable
{ {
if (!Configuration.CanModifyChance) if (!Configuration.CanModifyChance)
{ {
Error($"Cannot modify chance for role: {RoleName}"); Error($"Cannot modify chance for role: {RoleName.Translate()}");
return; return;
} }
@@ -166,7 +167,7 @@ public interface ICustomRole : IOptionable
return; return;
} }
Error($"Error getting chance configuration for role: {RoleName}"); Error($"Error getting chance configuration for role: {RoleName.Translate()}");
} }
/// <summary> /// <summary>
@@ -181,7 +182,7 @@ public interface ICustomRole : IOptionable
return; return;
} }
Error($"Error getting count configuration for role: {RoleName}"); Error($"Error getting count configuration for role: {RoleName.Translate()}");
} }
/// <summary> /// <summary>
@@ -221,7 +222,7 @@ public interface ICustomRole : IOptionable
/// <returns>A string with a custom ejection message or <see langword="null"/>.</returns> /// <returns>A string with a custom ejection message or <see langword="null"/>.</returns>
string? GetCustomEjectionMessage(NetworkedPlayerInfo player) string? GetCustomEjectionMessage(NetworkedPlayerInfo player)
{ {
return Team == ModdedRoleTeams.Impostor ? $"{player.PlayerName} was The {RoleName}" : null; return Team == ModdedRoleTeams.Impostor ? $"{player.PlayerName} was The {RoleName.Translate()}" : null;
} }
/// <summary> /// <summary>
+24
View File
@@ -0,0 +1,24 @@
namespace MiraAPI.Translation;
/// <summary>
/// Supported languages for Mira translation system.
/// Maps to Among Us's SupportedLangs enum values.
/// </summary>
public enum MiraLanguage
{
English,
Spanish,
Korean,
Russian,
Portuguese,
Arabic,
Filipino,
Polish,
SChinese,
TChinese,
French,
German,
Japanese,
Dutch,
Italian,
}
@@ -0,0 +1,15 @@
namespace MiraAPI.Translation;
/// <summary>
/// Extension method for translating strings.
/// </summary>
public static class TranslationExtensions
{
/// <summary>
/// Translates a key to the current language.
/// </summary>
public static string Translate(this string key)
{
return TranslationManager.Translate(key);
}
}
+205
View File
@@ -0,0 +1,205 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml.Linq;
using Reactor.Utilities;
using UnityEngine;
namespace MiraAPI.Translation;
public static class TranslationManager
{
private const string LangDirectory = "mira_languages";
private static readonly Dictionary<string, Dictionary<MiraLanguage, Dictionary<string, string>>> Translations = [];
/// <summary>
/// Gets the current language from Among Us settings.
/// </summary>
public static MiraLanguage CurrentLanguage
{
get
{
try
{
var langName = AmongUs.Data.DataManager.Settings.Language.CurrentLanguage.ToString();
return Enum.TryParse<MiraLanguage>(langName, out var result) ? result : MiraLanguage.English;
}
catch
{
return MiraLanguage.English;
}
}
}
/// <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>
public static void Register(string modGuid)
{
var callingAssembly = Assembly.GetCallingAssembly();
var dir = GetModLangDir(modGuid);
// Copy embedded XML resources to disk (first-run only)
CopyEmbeddedToDisk(callingAssembly, modGuid, dir);
// Load all XML files from disk into memory
LoadFromDisk(modGuid, dir);
}
/// <summary>
/// Translates a key into the current language.
/// Searches all mods, with reverse lookup fallback.
/// </summary>
public static string Translate(string key)
{
var currentLang = CurrentLanguage;
foreach (var (_, modTranslations) in Translations)
{
if (modTranslations.TryGetValue(currentLang, out var langDict) &&
langDict.TryGetValue(key, out var translated))
{
return translated;
}
}
foreach (var (_, modTranslations) in Translations)
{
if (modTranslations.TryGetValue(MiraLanguage.English, out var engDict) &&
engDict.TryGetValue(key, out var english))
{
return english;
}
}
string? bestStructuredKey = null;
foreach (var (_, modTranslations) in Translations)
{
if (modTranslations.TryGetValue(MiraLanguage.English, out var engDict))
{
foreach (var (structuredKey, englishValue) in engDict)
{
if (englishValue == key)
{
bestStructuredKey = structuredKey;
break;
}
}
}
if (bestStructuredKey != null) break;
}
if (bestStructuredKey != null)
{
foreach (var (_, modTranslations) in Translations)
{
if (modTranslations.TryGetValue(currentLang, out var langDict) &&
langDict.TryGetValue(bestStructuredKey, out var revTranslated))
{
return revTranslated;
}
}
foreach (var (_, modTranslations) in Translations)
{
if (modTranslations.TryGetValue(MiraLanguage.English, out var engDict) &&
engDict.TryGetValue(bestStructuredKey, out var revEnglish))
{
return revEnglish;
}
}
}
return key;
}
private static string GetModLangDir(string modGuid)
{
return Path.Combine(Application.persistentDataPath, LangDirectory, modGuid);
}
private static void CopyEmbeddedToDisk(Assembly assembly, string modGuid, string dir)
{
Directory.CreateDirectory(dir);
var resourcePrefix = $"{assembly.GetName().Name}.Resources.Translations.";
foreach (var resourceName in assembly.GetManifestResourceNames())
{
if (!resourceName.StartsWith(resourcePrefix)) continue;
var fileName = resourceName.Substring(resourcePrefix.Length);
var diskPath = Path.Combine(dir, fileName);
try
{
using var stream = assembly.GetManifestResourceStream(resourceName);
if (stream == null) continue;
using var fs = File.Create(diskPath);
stream.CopyTo(fs);
Info($"Installed translation: {modGuid}/{fileName}");
}
catch (Exception e)
{
Warning($"Failed to copy {resourceName}: {e.Message}");
}
}
}
private static void LoadFromDisk(string modGuid, string dir)
{
if (!Directory.Exists(dir)) return;
if (!Translations.ContainsKey(modGuid))
{
Translations[modGuid] = [];
}
foreach (var filePath in Directory.GetFiles(dir, "*.xml"))
{
var fileName = Path.GetFileNameWithoutExtension(filePath);
if (!Enum.TryParse<MiraLanguage>(fileName, out var lang)) continue;
try
{
var dict = ParseXmlFile(filePath);
Translations[modGuid][lang] = dict;
Info($"Loaded {lang} translation for mod {modGuid} ({dict.Count} keys)");
}
catch (Exception e)
{
Error($"Failed to load translation {filePath}: {e.Message}");
}
}
}
private static Dictionary<string, string> ParseXmlFile(string filePath)
{
var dict = new Dictionary<string, string>();
var doc = XDocument.Load(filePath);
if (doc.Root == null) return dict;
foreach (var element in doc.Root.Elements("string"))
{
var name = element.Attribute("name")?.Value;
var value = element.Value;
if (string.IsNullOrEmpty(name)) continue;
value = value.Replace("<nl>", "\n").Replace("<and>", "&");
dict[name] = value;
}
return dict;
}
}
+2 -1
View File
@@ -5,6 +5,7 @@ using System.Linq;
using HarmonyLib; using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppInterop.Runtime.InteropTypes.Arrays;
using MiraAPI.Roles; using MiraAPI.Roles;
using MiraAPI.Translation;
using MiraAPI.Utilities.Assets; using MiraAPI.Utilities.Assets;
using Rewired; using Rewired;
using TMPro; using TMPro;
@@ -536,7 +537,7 @@ public static class Helpers
{ {
if (role is ICustomRole custom) if (role is ICustomRole custom)
{ {
return custom.RoleName; return custom.RoleName.Translate();
} }
return role.NiceName; return role.NiceName;
} }