Wrap up locale system

This commit is contained in:
Atony committed 2026-08-27 23:51:25 -05:00
1 parent 709b59300b
commit 648e9ec185
14 files changed
+91 -66

No files matched your search

+3 -3
View File
@@ -2,7 +2,7 @@
using MiraAPI.GameOptions;
using MiraAPI.Modifiers;
using MiraAPI.Modifiers.Types;
using MiraAPI.Translation;
using MiraAPI.Utilities.Assets;
using UnityEngine;
@@ -10,7 +10,7 @@ namespace MiraAPI.Example.Modifiers;
public class CaptainModifier : GameModifier
{
public override string ModifierName => "modifier.captain";
public override string ModifierName => MiraLocaleManager.Get("modifier.captain");
public override LoadableAsset<Sprite>? ModifierIcon => ExampleAssets.CallMeetingButton;
public override void OnDeath(DeathReason reason)
@@ -20,7 +20,7 @@ public class CaptainModifier : GameModifier
public override string GetDescription()
{
return "modifier.captain.description";
return MiraLocaleManager.Get("modifier.captain.TabDescription");
}
public override int GetAssignmentChance()
@@ -1,13 +1,14 @@
using Il2CppSystem;
using MiraAPI.Example.Roles;
using MiraAPI.Modifiers.Types;
using MiraAPI.Translation;
using UnityEngine;
namespace MiraAPI.Example.Modifiers.Freezer;
public class FreezeModifier : TimedModifier
{
public override string ModifierName => "modifier.freezed";
public override string ModifierName => MiraLocaleManager.Get("modifier.freezed");
public override bool HideOnUi => false;
public override float Duration => 15f;
public override bool AutoStart => true;
@@ -1,14 +1,15 @@
using MiraAPI.Modifiers.Types;
using MiraAPI.Translation;
namespace MiraAPI.Example.Modifiers;
public class HighPriorityModifier : GameModifier
{
public override string ModifierName => "modifier.highPriority";
public override string ModifierName => MiraLocaleManager.Get("modifier.highPriority");
public override string GetDescription()
{
return "modifier.highPriority.description";
return MiraLocaleManager.Get("modifier.highPriority.TabDescription");
}
public override int GetAssignmentChance()
+26 -25
View File
@@ -1,33 +1,34 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="ApiExample.Role.Teleporter">Teleporter</string>
<string name="ApiExample.Role.Teleporter.IntroBlurb">Zoom out and teleport across the map!</string>
<string name="ApiExample.Role.Teleporter.MedDescription">Zoom out and teleport across the map!</string>
<string name="ApiExample.Role.Teleporter.TabDescription">Zoom out and teleport across the map!</string>
<string name="ApiExample.Role.Teleporter.WikiDescription">The Teleporter is a Crewmate role that can move around the map using their dope-ass ability.</string>
<string name="ApiExample.Role.Freezer">Freezer</string>
<string name="ApiExample.Role.Freezer.IntroBlurb">Get iced, nerd.</string>
<string name="ApiExample.Role.Freezer.MedDescription">Freeze others for a duration of time.</string>
<string name="ApiExample.Role.Freezer.TabDescription">Freeze another player for a duration of time.</string>
<string name="ApiExample.Role.Freezer.WikiDescription">The Freezer is an Impostor role that can freeze players. Or something along those lines.</string>
<string name="ApiExample.Role.Mayor">Mayor</string>
<string name="ApiExample.Role.Mayor.IntroBlurb">Democracy is the best policy.</string>
<string name="ApiExample.Role.Mayor.MedDescription">Vote out the Impostors with more votes.
<string name="ApiExample.Role.Crewmate.Teleporter">Teleporter</string>
<string name="ApiExample.Role.Crewmate.Teleporter.IntroBlurb">Zoom out and teleport across the map!</string>
<string name="ApiExample.Role.Crewmate.Teleporter.MedDescription">Zoom out and teleport across the map!</string>
<string name="ApiExample.Role.Crewmate.Teleporter.TabDescription">Zoom out and teleport across the map!</string>
<string name="ApiExample.Role.Crewmate.Teleporter.WikiDescription">The Teleporter is a Crewmate role that can move around the map using their dope-ass ability.</string>
<string name="ApiExample.Role.Impostor.Freezer">Freezer</string>
<string name="ApiExample.Role.Impostor.Freezer.IntroBlurb">Get iced, nerd.</string>
<string name="ApiExample.Role.Impostor.Freezer.MedDescription">Freeze others for a duration of time.</string>
<string name="ApiExample.Role.Impostor.Freezer.TabDescription">Freeze another player for a duration of time.</string>
<string name="ApiExample.Role.Impostor.Freezer.WikiDescription">The Freezer is an Impostor role that can freeze players. Or something along those lines.</string>
<string name="ApiExample.Role.Crewmate.Mayor">Mayor</string>
<string name="ApiExample.Role.Crewmate.Mayor.IntroBlurb">Democracy is the best policy.</string>
<string name="ApiExample.Role.Crewmate.Mayor.MedDescription">Vote out the Impostors with more votes.
Your votes have double the power.</string>
<string name="ApiExample.Role.Mayor.TabDescription">You get one extra vote in the meeting!</string>
<string name="ApiExample.Role.Mayor.WikiDescription">The Mayor is a Crewmate role that gets two votes in the meeting, instead of the singular vote other Crewmates get.</string>
<string name="ApiExample.Role.Chamelon">Chamelon</string>
<string name="ApiExample.Role.Chamelon.IntroBlurb">Become one with the environment.</string>
<string name="ApiExample.Role.Chamelon.MedDescription">Fade out of everyone's vision by not moving.</string>
<string name="ApiExample.Role.Chamelon.TabDescription">Stay invisible while not moving.</string>
<string name="ApiExample.Role.Chamelon.WikiDescription">The Chamelon is a Crewmate role that slowly becomes invisible while standing still.</string>
<string name="ApiExample.Role.OutcastKiller">Outcast Killer</string>
<string name="ApiExample.Role.OutcastKiller.IntroBlurb">Kill everyone else.</string>
<string name="ApiExample.Role.OutcastKiller.MedDescription">Be the last outcast standing.
<string name="ApiExample.Role.Crewmate.Mayor.TabDescription">You get one extra vote in the meeting!</string>
<string name="ApiExample.Role.Crewmate.Mayor.WikiDescription">The Mayor is a Crewmate role that gets two votes in the meeting, instead of the singular vote other Crewmates get.</string>
<string name="ApiExample.Role.Crewmate.Chamelon">Chamelon</string>
<string name="ApiExample.Role.Crewmate.Chamelon.IntroBlurb">Become one with the environment.</string>
<string name="ApiExample.Role.Crewmate.Chamelon.MedDescription">Fade out of everyone's vision by not moving.</string>
<string name="ApiExample.Role.Crewmate.Chamelon.TabDescription">Stay invisible while not moving.</string>
<string name="ApiExample.Role.Crewmate.Chamelon.WikiDescription">The Chamelon is a Crewmate role that slowly becomes invisible while standing still.</string>
<string name="ApiExample.Role.Outcast.OutcastKiller">Outcast Killer</string>
<string name="ApiExample.Role.Outcast.OutcastKiller.IntroBlurb">Kill everyone else.</string>
<string name="ApiExample.Role.Outcast.OutcastKiller.MedDescription">Be the last outcast standing.
Eliminate every other faction.</string>
<string name="ApiExample.Role.OutcastKiller.TabDescription">Outcast who can kill.</string>
<string name="ApiExample.Role.OutcastKiller.WikiDescription">The Outcast Killer is an Outcast role that can kill, like some sort of neutral killer...</string>
<string name="ApiExample.Role.Outcast.OutcastKiller.TabDescription">Outcast who can kill.</string>
<string name="ApiExample.Role.Outcast.OutcastKiller.WikiDescription">The Outcast Killer is an Outcast role that can kill, like some sort of neutral killer...</string>
<string name="outcast">Outcast</string>
<string name="outcast.group">Outcast Roles</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">Think</string>
+8 -10
View File
@@ -6,12 +6,12 @@
<string name="ApiExample.Role.Teleporter.TabDescription">缩小视野并在地图上传送!</string>
<string name="ApiExample.Role.Freezer">冰冻者</string>
<string name="ApiExample.Role.Freezer.TabDescription">将其他玩家冰冻一段时间。</string>
<string name="mayorrole">市长</string>
<string name="mayorrole.description">你拥有额外投票权!</string>
<string name="chamelonrole">变色龙</string>
<string name="chamelonrole.description">静止不动时保持隐身。</string>
<string name="outcastKillerrole">流放杀手</string>
<string name="outcastKillerrole.description">可以击杀的流放者。</string>
<string name="ApiExample.Role.Crewmate.Mayor">市长</string>
<string name="ApiExample.Role.Crewmate.Mayor.TabDescription">你拥有额外投票权!</string>
<string name="ApiExample.Role.Crewmate.Chamelon">变色龙</string>
<string name="ApiExample.Role.Crewmate.Chamelon.TabDescription">静止不动时保持隐身。</string>
<string name="ApiExample.Role.Outcast.OutcastKiller">流放杀手</string>
<string name="ApiExample.Role.Outcast.OutcastKiller.TabDescription">可以击杀的流放者。</string>
<string name="outcast">流放者</string>
<string name="outcast.roleTitle">流放者</string>
<string name="outcast.teamIntroDescription">你是流放者,你没有队伍。</string>
@@ -22,10 +22,10 @@
<string name="button.callMeeting">召集会议</string>
<string name="button.winGame">赢得游戏</string>
<string name="modifier.captain">队长</string>
<string name="modifier.captain.description">你可以在地图上任意位置召集会议。</string>
<string name="modifier.captain.TabDescription">你可以在地图上任意位置召集会议。</string>
<string name="modifier.freezed">已被冰冻</string>
<string name="modifier.highPriority">高优先级</string>
<string name="modifier.highPriority.description">你是高优先级!老实说我也不太清楚这是什么意思……</string>
<string name="modifier.highPriority.TabDescription">你是高优先级!老实说我也不太清楚这是什么意思……</string>
<string name="options.exampleOptions1">示例选项组 1</string>
<string name="options.exampleOptions1.toggleOpt1">开关选项 1</string>
<string name="options.exampleOptions1.toggleOpt2">开关选项 2</string>
@@ -46,10 +46,8 @@
<string name="options.generalModifier">通用</string>
<string name="options.generalModifier.someBooleanOption">某个布尔选项</string>
<string name="options.generalModifier.numberOption">数字选项</string>
<string name="options.freezerRoleSettings">自定义职业</string>
<string name="ApiExample.Role.Freezer.Settings.FreezeDuration">冰冻时长</string>
<string name="ApiExample.Role.Freezer.Settings.FreezeUses">冰冻次数</string>
<string name="ApiExample.Role.Teleporter.Settings">传送者</string>
<string name="ApiExample.Role.Teleporter.Settings.TeleportCooldown">传送冷却</string>
<string name="ApiExample.Role.Teleporter.Settings.TeleportDuration">传送持续时间</string>
<string name="ApiExample.Role.Teleporter.Settings.ZoomDistance">缩放距离</string>
+14 -4
View File
@@ -10,7 +10,8 @@ namespace MiraAPI.Example.Roles;
public class ChameloenRole : CrewmateRole, ICustomRole
{
public string IdPart => "ApiExample.Role.Chamelon";
public string IdPart => "Chamelon";
public string IdPrefix => "ApiExample.Role.Crewmate";
public Color RoleColor => Palette.AcceptedGreen;
public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate;
@@ -20,7 +21,9 @@ public class ChameloenRole : CrewmateRole, ICustomRole
{
OptionsScreenshot = ExampleAssets.Banner,
Icon = MiraAssets.CrewmateFile,
IconTmp = TmpSpriteUtils.CreateSpriteAsset(MiraAssets.CrewmateFile.LoadAsset(), "ApiExample.Role.Impostor.ChameleonRole"),
IconTmp = TmpSpriteUtils.CreateSpriteAsset(
MiraAssets.CrewmateFile.LoadAsset(),
"ApiExample.Role.Impostor.ChameleonRole"),
IntroSound = CustomRoleUtils.GetIntroSound(RoleTypes.Shapeshifter),
};
@@ -54,12 +57,18 @@ public class ChameloenRole : CrewmateRole, ICustomRole
{
SpriteRenderer rend = Player.cosmetics.currentBodySprite.BodySprite;
TextMeshPro tmp = Player.cosmetics.nameText;
tmp.color = Color.Lerp(tmp.color, new Color(tmp.color.r, tmp.color.g, tmp.color.b, Player.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
tmp.color = Color.Lerp(
tmp.color,
new Color(tmp.color.r, tmp.color.g, tmp.color.b, Player.AmOwner ? 0.3f : 0),
Time.deltaTime * 4f);
rend.color = Color.Lerp(rend.color, new Color(1, 1, 1, Player.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
foreach (var cosmetic in Player.cosmetics.transform.GetComponentsInChildren<SpriteRenderer>())
{
cosmetic.color = Color.Lerp(cosmetic.color, new Color(1, 1, 1, Player.AmOwner ? 0.3f : 0), Time.deltaTime * 4f);
cosmetic.color = Color.Lerp(
cosmetic.color,
new Color(1, 1, 1, Player.AmOwner ? 0.3f : 0),
Time.deltaTime * 4f);
}
}
}
@@ -73,6 +82,7 @@ public class ChameloenRole : CrewmateRole, ICustomRole
{
cosmetic.color = Color.white;
}
Player.cosmetics.currentBodySprite.BodySprite.color = Color.white;
Player.cosmetics.nameText.color = Color.white;
}
+3 -2
View File
@@ -6,14 +6,15 @@ namespace MiraAPI.Example.Roles;
public class FreezerRole : ImpostorRole, ICustomRole
{
public string IdPart => "ApiExample.Role.Freezer";
public string IdPart => "Freezer";
public string IdPrefix => "ApiExample.Role.Impostor";
public Color RoleColor => Palette.Blue;
public ModdedRoleTeams Team => ModdedRoleTeams.Impostor;
public CustomRoleConfiguration Configuration => new CustomRoleConfiguration(this)
{
Icon = MiraAssets.ImpostorFile,
IconTmp = TmpSpriteUtils.CreateSpriteAsset(MiraAssets.ImpostorFile.LoadAsset(), "ApiExample.Role.Impostor.FreezerRole"),
IconTmp = TmpSpriteUtils.CreateSpriteAsset(MiraAssets.ImpostorFile.LoadAsset(), "ApiExample.Role.Impostor.Freezer"),
OptionsScreenshot = ExampleAssets.Banner,
MaxRoleCount = 2,
};
+2 -1
View File
@@ -5,7 +5,8 @@ namespace MiraAPI.Example.Roles;
public class MayorRole : CrewmateRole, ICustomRole
{
public string IdPart => "ApiExample.Role.Mayor";
public string IdPart => "Mayor";
public string IdPrefix => "ApiExample.Role.Crewmate";
public Color RoleColor => new Color32(221, 176, 152, 255);
public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate;
public CustomRoleConfiguration Configuration => new(this);
+3 -2
View File
@@ -8,7 +8,8 @@ namespace MiraAPI.Example.Roles;
public class NeutralKillerRole : ImpostorRole, ICustomRole
{
public string IdPart => "ApiExample.Role.OutcastKiller";
public string IdPart => "OutcastKiller";
public string IdPrefix => "ApiExample.Role.Outcast";
public Color RoleColor => Color.magenta;
public ModdedRoleTeams Team => ModdedRoleTeams.Custom;
@@ -19,7 +20,7 @@ public class NeutralKillerRole : ImpostorRole, ICustomRole
CanUseVent = true,
};
public RoleOptionsGroup RoleOptionsGroup { get; } = new("outcast", Color.gray);
public RoleOptionsGroup RoleOptionsGroup { get; } = new("outcast.group", Color.gray);
[HideFromIl2Cpp]
public TeamIntroConfiguration? IntroConfiguration { get; } = new(
+2 -1
View File
@@ -5,7 +5,8 @@ namespace MiraAPI.Example.Roles;
public class TeleporterRole : CrewmateRole, ICustomRole
{
public string IdPart => "ApiExample.Role.Teleporter";
public string IdPart => "Teleporter";
public string IdPrefix => "ApiExample.Role.Crewmate";
public Color RoleColor => new Color32(221, 176, 152, 255);
public ModdedRoleTeams Team => ModdedRoleTeams.Crewmate;
+5 -5
View File
@@ -78,7 +78,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
}
catch (Exception e)
{
Error($"Error while deactivating modifier {modifier.ModifierName.Translate()}: {e.ToString()}");
Error($"Error while deactivating modifier {modifier.ModifierName}: {e.ToString()}");
}
Modifiers.Remove(modifier);
@@ -96,7 +96,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
}
catch (Exception e)
{
Error($"Error while activating modifier {modifier.ModifierName.Translate()}: {e.ToString()}");
Error($"Error while activating modifier {modifier.ModifierName}: {e.ToString()}");
}
if (modifier is TimedModifier { AutoStart: true } timer)
@@ -123,7 +123,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
}
catch (Exception e)
{
Error($"Error while (fixed) updating modifier {modifier.ModifierName.Translate()}: {e.ToString()}");
Error($"Error while (fixed) updating modifier {modifier.ModifierName}: {e.ToString()}");
}
}
@@ -154,7 +154,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
}
catch (Exception e)
{
Error($"Error while updating modifier {modifier.ModifierName.Translate()}: {e.ToString()}");
Error($"Error while updating modifier {modifier.ModifierName}: {e.ToString()}");
}
}
}
@@ -353,7 +353,7 @@ public class ModifierComponent(IntPtr cppPtr) : MonoBehaviour(cppPtr)
{
if (!ActiveModifiers.Contains(modifier))
{
Error($"Cannot remove modifier {modifier.ModifierName.Translate()} because it is not active on this player.");
Error($"Cannot remove modifier {modifier.ModifierName} because it is not active on this player.");
return;
}
@@ -51,8 +51,8 @@ public class ModifierUiComponent(nint cppPtr) : MonoBehaviour(cppPtr)
return;
}
nameText.text = Modifier.ModifierName.Translate();
desc.text = Modifier.GetDescription().Translate();
nameText.text = Modifier.ModifierName;
desc.text = Modifier.GetDescription();
modBg.gameObject.SetActive(true);
icon.gameObject.SetActive(Modifier.ModifierIcon != null);
+11 -6
View File
@@ -15,10 +15,15 @@ namespace MiraAPI.Roles;
public interface ICustomRole : IOptionable
{
/// <summary>
/// Gets the id part used to build the role's translation keys.
/// Gets the id part used to build the role's translation keys. It is recommended to simply call it what the role is, as other mods may utilize it.
/// </summary>
string IdPart => GetType().Name;
/// <summary>
/// Gets the id part used to build the role's translation keys. It is recommended to call it ModGuid.Role.Faction
/// </summary>
string IdPrefix => GetType().Namespace!;
/// <summary>
/// Gets the name of the role.
/// </summary>
@@ -27,7 +32,7 @@ public interface ICustomRole : IOptionable
/// <summary>
/// Gets the role's name id for localization.
/// </summary>
string RoleNameLocale => IdPart;
string RoleNameLocale => MiraLocaleManager.BuildTranslationId(IdPrefix, IdPart);
/// <summary>
/// Gets the description of the role. Used in the Intro Cutscene.
@@ -37,7 +42,7 @@ public interface ICustomRole : IOptionable
/// <summary>
/// Gets the role's intro blurb id for localization.
/// </summary>
string RoleDescriptionLocale => MiraLocaleManager.BuildTranslationId(IdPart, "IntroBlurb");
string RoleDescriptionLocale => MiraLocaleManager.BuildTranslationId(IdPrefix, IdPart, "IntroBlurb");
/// <summary>
/// Gets the medium description of the role. Used in the role guide and options menu.
@@ -47,7 +52,7 @@ public interface ICustomRole : IOptionable
/// <summary>
/// Gets the role's medium description id for localization.
/// </summary>
string RoleMedDescriptionLocale => MiraLocaleManager.BuildTranslationId(IdPart, "MedDescription");
string RoleMedDescriptionLocale => MiraLocaleManager.BuildTranslationId(IdPrefix, IdPart, "MedDescription");
/// <summary>
/// Gets the long description of the role. Used in the Role Tab.
@@ -57,7 +62,7 @@ public interface ICustomRole : IOptionable
/// <summary>
/// Gets the role's long description id for localization.
/// </summary>
string RoleLongDescriptionLocale => MiraLocaleManager.BuildTranslationId(IdPart, "TabDescription");
string RoleLongDescriptionLocale => MiraLocaleManager.BuildTranslationId(IdPrefix, IdPart, "TabDescription");
/// <summary>
/// Gets the wiki description of the role. Used in the wiki, but not currently required.
@@ -67,7 +72,7 @@ public interface ICustomRole : IOptionable
/// <summary>
/// Gets the role's wiki description id for localization.
/// </summary>
string RoleWikiDescriptionLocale => MiraLocaleManager.BuildTranslationId(IdPart, "WikiDescription");
string RoleWikiDescriptionLocale => MiraLocaleManager.BuildTranslationId(IdPrefix, IdPart, "WikiDescription");
/// <summary>
/// Gets the faction / alignment of the role. Used in the role guide.
+7 -2
View File
@@ -187,9 +187,14 @@ public static class MiraLocaleManager
return fallback != string.Empty ? fallback : key;
}
public static string BuildTranslationId(string idPart, string suffix)
public static string BuildTranslationId(string modId, string idPart, string suffix)
{
return idPart.StartsWith('#') ? idPart[1..] : $"{idPart}.{suffix}";
return idPart.StartsWith('#') ? idPart[1..] : $"{modId}.{idPart}.{suffix}";
}
public static string BuildTranslationId(string modId, string idPart)
{
return idPart.StartsWith('#') ? idPart[1..] : $"{modId}.{idPart}";
}
private static string GetModLangDir(string modGuid)