using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using ETGGUI;
using HarmonyLib;
using Mono.Cecil.Cil;
using MonoMod.Cil;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SGUI;
using UnityEngine;
using UnityEngine.Networking;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("AutoTranslate")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("AutoTranslate")]
[assembly: AssemblyCopyright("Copyright © 2023")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("825bc8e2-2faf-496c-803c-3f3e31eba1a0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace AutoTranslate;
internal class AssetBundleLoader
{
public static AssetBundle LoadAssetBundle(string filePath)
{
AssetBundle result = null;
if (File.Exists(filePath))
{
try
{
result = AssetBundle.LoadFromFile(filePath);
Debug.Log((object)"已成功加载AssetBundle!Successfully loaded AssetBundle!");
}
catch (Exception ex)
{
Debug.LogError((object)"从文件加载AssetBundle失败。Failed loading AssetBundle from file.");
Debug.LogError((object)ex.ToString());
}
}
else
{
Debug.LogError((object)"AssetBundle不存在!AssetBundle does not exist!");
}
return result;
}
}
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInPlugin("kleirof.etg.autotranslate", "Auto Translate", "1.0.5")]
public class AutoTranslateModule : BaseUnityPlugin
{
public enum TranslationAPIType
{
Tencent,
Baidu,
Azure
}
public enum OverrideFontType
{
None,
Chinese,
English,
Japanese,
Korean,
Russian,
Polish,
Custom
}
public const string GUID = "kleirof.etg.autotranslate";
public const string NAME = "Auto Translate";
public const string VERSION = "1.0.5";
public const string TEXT_COLOR = "#AA3399";
public static AutoTranslateModule instance;
private ConfigEntry<bool> AcceptedModDeclaration;
private ConfigEntry<TranslationAPIType> TranslationAPI;
private ConfigEntry<string> RegexForFullTextNeedToTranslate;
private ConfigEntry<string> RegexForEachLineNeedToTranslate;
private ConfigEntry<string> RegexForIgnoredSubstringWithinText;
private ConfigEntry<int> RequestBatchSize;
private ConfigEntry<int> MaxRetryCount;
private ConfigEntry<int> TranslationCacheCapacity;
private ConfigEntry<bool> TranslateTextsOfItemTipsMod;
private ConfigEntry<string> PresetTranslations;
private ConfigEntry<OverrideFontType> OverrideFont;
private ConfigEntry<string> FontAssetBundleName;
private ConfigEntry<string> CustomDfFontName;
private ConfigEntry<string> CustomTk2dFontName;
private ConfigEntry<string> RegexForDfTokenizer;
private ConfigEntry<bool> LogRequestedTexts;
private ConfigEntry<string> TencentSecretId;
private ConfigEntry<string> TencentSecretKey;
private ConfigEntry<string> TencentSourceLanguage;
private ConfigEntry<string> TencentTargetLanguage;
private ConfigEntry<string> TencentRegion;
private ConfigEntry<string> BaiduAppId;
private ConfigEntry<string> BaiduSecretKey;
private ConfigEntry<string> BaiduSourceLanguage;
private ConfigEntry<string> BaiduTargetLanguage;
private ConfigEntry<string> AzureSubscriptionKey;
private ConfigEntry<string> AzureSourceLanguage;
private ConfigEntry<string> AzureTargetLanguage;
private ConfigEntry<string> AzureRegion;
private Harmony harmony;
private bool isConfigValid;
private GameObject autoTranslateObject;
internal TranslationManager translateManager;
internal Type itemTipsModuleType;
internal FontManager fontManager;
public void Start()
{
//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: Expected O, but got Unknown
//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Expected O, but got Unknown
ETGModMainBehaviour.WaitForGameManagerStart((Action<GameManager>)GMStart);
instance = this;
TranslationManagerConfig config = InitializeConfigs();
if (!AcceptedModDeclaration.Value)
{
Debug.LogError((object)"AutoTranslate: 还未接受Mod声明!Mod declaration not accepted!");
return;
}
if (!isConfigValid)
{
Debug.LogError((object)"AutoTranslate: 翻译配置无效!Invalid Translate Config!");
return;
}
fontManager = new FontManager(OverrideFont.Value, FontAssetBundleName.Value, CustomDfFontName.Value, CustomTk2dFontName.Value, RegexForDfTokenizer.Value);
harmony = new Harmony("kleirof.etg.autotranslate");
harmony.PatchAll();
autoTranslateObject = new GameObject("Auto Translate Object");
Object.DontDestroyOnLoad((Object)(object)autoTranslateObject);
translateManager = autoTranslateObject.AddComponent<TranslationManager>();
translateManager.Initialize(config);
DoOptionalPatches();
}
internal static void Log(string text, string color = "FFFFFF")
{
ETGModConsole.Log((object)("<color=" + color + ">" + text + "</color>"), false);
}
internal void GMStart(GameManager g)
{
if (!AcceptedModDeclaration.Value)
{
Log("Auto Translate v1.0.5 started, but AcceptedModDeclaration not checked!", "#FF0000");
Log("(Important!) Please read the declaration on mod website and then check the config in mod manager or manually edit it.", "#FF0000");
}
else if (!isConfigValid)
{
Log("Auto Translate v1.0.5 started, but config invalid!", "#FF0000");
Log("Please check the config in mod manager or manually edit it.", "#FF0000");
}
else
{
Log("Auto Translate v1.0.5 started successfully.", "#AA3399");
fontManager?.InitializeFontAfterGameManager(OverrideFont.Value);
}
}
private TranslationManagerConfig InitializeConfigs()
{
AcceptedModDeclaration = ((BaseUnityPlugin)this).Config.Bind<bool>("1.General", "AcceptedModDeclaration", false, "作为Mod用户,我已阅读并同意网站上此mod的声明,清楚潜在费用的去向并信任此mod的行为,并对自己的选择负责任。As a mod user, I have read and accepted the declaration on this mod's website, understand the potential destination of the fees, trust the actions of this mod, and take responsibility for my choice.");
TranslationAPI = ((BaseUnityPlugin)this).Config.Bind<TranslationAPIType>("1.General", "TranslationAPI", TranslationAPIType.Tencent, "选择使用的翻译API。Choose the translation API to use.");
RegexForFullTextNeedToTranslate = ((BaseUnityPlugin)this).Config.Bind<string>("1.General", "RegexForFullTextNeedToTranslate", "^(?!Enter the Gungeon).*$", "正则表达式,一个多行文本若匹配为真,则这个多行文本保留以待翻译。用来筛选待翻译的文本以节省翻译额度。Regular expression, if a multiple line text matches true, then this multiple line text is retained for translation. Used to filter the text to be translated to save translation quotas.");
RegexForEachLineNeedToTranslate = ((BaseUnityPlugin)this).Config.Bind<string>("1.General", "RegexForEachLineNeedToTranslate", "^(?![@#])(?=\\S)(?!^[\\d\\p{P}]+$)(?!.*[\\u4e00-\\u9fa5\\u3000-\\u303F\\uFF00-\\uFFEF]).*$", "正则表达式,多行文本若存在一行匹配,整个多行文本保留以待翻译。用来筛选待翻译的文本以节省翻译额度。Regular expression, if there is a matching line in multiple lines of text, the entire multiple lines of text are retained for translation. Used to filter the text to be translated to save translation quotas.");
RegexForIgnoredSubstringWithinText = ((BaseUnityPlugin)this).Config.Bind<string>("1.General", "RegexForIgnoredSubstringWithinText", "(?:\\[[^\\]]*\\])|(?:\\{[^}]*\\})|(?:\\^[\\w\\d]{9})|(?:[\\u4e00-\\u9fa5\\u3000-\\u303F\\uFF00-\\uFFEF]+)", "正则表达式,匹配文本中需要忽略的子文本。请使用非捕获组。这通常包括一些要特殊处理的贴图和转义符。Regular expression, matching sub texts that need to be ignored in the text. Please use non capture groups. This usually includes some textures and escape characters that require special handling.");
RequestBatchSize = ((BaseUnityPlugin)this).Config.Bind<int>("1.General", "RequestBatchSize", 1024, "发送请求的批量数据总大小。若翻译api提示单次请求过长,请减小此值。The total size of batch data for sending requests. If the translation API prompts that a single request is too long, please reduce this value.");
MaxRetryCount = ((BaseUnityPlugin)this).Config.Bind<int>("1.General", "MaxRetryCount", 3, "发生错误时的最大重试次数。The maximum number of retries when an error occurs.");
TranslationCacheCapacity = ((BaseUnityPlugin)this).Config.Bind<int>("1.General", "TranslationCacheCapacity", 1024, "最大翻译缓存容量。Maximum translation cache capacity.");
TranslateTextsOfItemTipsMod = ((BaseUnityPlugin)this).Config.Bind<bool>("1.General", "TranslateTextsOfItemTipsMod", true, "翻译ItemTipsMod中的文本。Translate the text in ItemTipsMod.");
PresetTranslations = ((BaseUnityPlugin)this).Config.Bind<string>("1.General", "PresetTranslations", "PresetTranslations.json", "预设翻译的文件名。使用预设翻译以减少加载时常见文本的翻译请求,留空表示不使用。预设翻译为位于dll同目录下的json文件。The file name for the preset translation. Use preset translation to reduce translation requests for common text during loading, leaving blank to indicate not using. The preset translation is a JSON file located in the same directory as the DLL.");
OverrideFont = ((BaseUnityPlugin)this).Config.Bind<OverrideFontType>("1.General", "OverrideFont", OverrideFontType.Chinese, "用来覆盖游戏字体的字体。根据你需要的目标语言选择。Font used to override the font of the game. Choose according to the target language you need.");
FontAssetBundleName = ((BaseUnityPlugin)this).Config.Bind<string>("1.General", "FontAssetBundleName", "", "包含自定义字体的AssetBundle名称。位于dll同目录下。AssetBundle name containing custom fonts. Located in the same directory as DLL.");
CustomDfFontName = ((BaseUnityPlugin)this).Config.Bind<string>("1.General", "CustomDfFontName", "", "要使用的自定义df字体。请把它包含于FontAssetBundle。The custom df font to be used. Please include it in FontAssetBundle.");
CustomTk2dFontName = ((BaseUnityPlugin)this).Config.Bind<string>("1.General", "CustomTk2dFontName", "", "要使用的自定义tk2d字体。请把它包含于FontAssetBundle。The custom tk2d font to be used. Please include it in FontAssetBundle.");
RegexForDfTokenizer = ((BaseUnityPlugin)this).Config.Bind<string>("1.General", "RegexForDfTokenizer", FontManager.defaultRegexForTokenizer, "用于df生成token的正则表达式。token用于处理文本的自动换行位置。如每个字换行还是单词后换行。参考默认样例填写。建议只修改Text相关内容。A regular expression used for generating tokens from df. Token is used to handle the automatic line break position of text. Whether to wrap each word or to wrap after each word. Fill in according to the default example. Suggest only modifying content related to Text.");
LogRequestedTexts = ((BaseUnityPlugin)this).Config.Bind<bool>("1.General", "LogRequestedTexts", false, "在日志中显示请求翻译的文本。Log the text requested for translation.");
TencentSecretId = ((BaseUnityPlugin)this).Config.Bind<string>("2.TencentTranslation", "SecretId", "", "腾讯翻译的SecretId。The SecretId of Tencent Translate.");
TencentSecretKey = ((BaseUnityPlugin)this).Config.Bind<string>("2.TencentTranslation", "SecretKey", "", "腾讯翻译的SecretKey。The SecretKey of Tencent Translate.");
TencentSourceLanguage = ((BaseUnityPlugin)this).Config.Bind<string>("2.TencentTranslation", "SourceLanguage", "en", "腾讯翻译的源语言,如en。The source language of Tencent Translate, such as en.");
TencentTargetLanguage = ((BaseUnityPlugin)this).Config.Bind<string>("2.TencentTranslation", "TargetLanguage", "zh", "腾讯翻译的目标语言,如zh。Tencent Translate's target language, such as zh.");
TencentRegion = ((BaseUnityPlugin)this).Config.Bind<string>("2.TencentTranslation", "Region", "ap-beijing", "地域,用来标识希望连接哪个地域的服务器,如ap-beijing。Region, used to identify which region's server you want to connect to, such as ap-beijing.");
BaiduAppId = ((BaseUnityPlugin)this).Config.Bind<string>("3.BaiduTranslate", "SecretId", "", "百度翻译的AppId。The AppId of Baidu Translate.");
BaiduSecretKey = ((BaseUnityPlugin)this).Config.Bind<string>("3.BaiduTranslate", "SecretKey", "", "百度翻译的SecretKey。The SecretKey for Baidu Translate.");
BaiduSourceLanguage = ((BaseUnityPlugin)this).Config.Bind<string>("3.BaiduTranslate", "SourceLanguage", "en", "百度翻译的源语言,如en。The source language of Baidu Translate, such as en.");
BaiduTargetLanguage = ((BaseUnityPlugin)this).Config.Bind<string>("3.BaiduTranslate", "TargetLanguage", "zh", "百度翻译的目标语言,如zh。The target language for Baidu Translate, such as zh.");
AzureSubscriptionKey = ((BaseUnityPlugin)this).Config.Bind<string>("4.AzureTranslate", "AzureSubscriptionKey", "", "Azure翻译的SubscriptionKey。Subscription Key for Azure Translation.");
AzureSourceLanguage = ((BaseUnityPlugin)this).Config.Bind<string>("4.AzureTranslate", "SourceLanguage", "en", "Azure翻译的源语言,如en。The source language for Azure translation, such as en.");
AzureTargetLanguage = ((BaseUnityPlugin)this).Config.Bind<string>("4.AzureTranslate", "TargetLanguage", "zh-Hans", "Azure翻译的目标语言,如zh-Hans。The target language for Azure translation, such as zh-Hans.");
AzureRegion = ((BaseUnityPlugin)this).Config.Bind<string>("4.AzureTranslate", "AzureRegion", "global", "地域,用来标识希望连接哪个地域的服务器,如global。Region, used to identify which region's server you want to connect to, such as global.");
TranslationManagerConfig translationManagerConfig = new TranslationManagerConfig
{
TranslationAPI = TranslationAPI.Value,
RegexForFullTextNeedToTranslate = RegexForFullTextNeedToTranslate.Value,
RegexForEachLineNeedToTranslate = RegexForEachLineNeedToTranslate.Value,
RegexForIgnoredSubstringWithinText = RegexForIgnoredSubstringWithinText.Value,
RequestBatchSize = RequestBatchSize.Value,
MaxRetryCount = MaxRetryCount.Value,
TranslationCacheCapacity = TranslationCacheCapacity.Value,
PresetTranslations = PresetTranslations.Value,
LogRequestedTexts = LogRequestedTexts.Value,
TencentSecretId = TencentSecretId.Value,
TencentSecretKey = TencentSecretKey.Value,
TencentSourceLanguage = TencentSourceLanguage.Value,
TencentTargetLanguage = TencentTargetLanguage.Value,
TencentRegion = TencentRegion.Value,
BaiduAppId = BaiduAppId.Value,
BaiduSecretKey = BaiduSecretKey.Value,
BaiduSourceLanguage = BaiduSourceLanguage.Value,
BaiduTargetLanguage = BaiduTargetLanguage.Value,
AzureSubscriptionKey = AzureSubscriptionKey.Value,
AzureSourceLanguage = AzureSourceLanguage.Value,
AzureTargetLanguage = AzureTargetLanguage.Value,
AzureRegion = AzureRegion.Value
};
isConfigValid = translationManagerConfig.CheckConfigValues();
return translationManagerConfig;
}
private void DoOptionalPatches()
{
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_007a: Expected O, but got Unknown
//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
//IL_00bf: Expected O, but got Unknown
//IL_0132: Unknown result type (might be due to invalid IL or missing references)
//IL_0140: Expected O, but got Unknown
//IL_0177: Unknown result type (might be due to invalid IL or missing references)
//IL_0181: Expected O, but got Unknown
//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
//IL_0207: Expected O, but got Unknown
//IL_023e: Unknown result type (might be due to invalid IL or missing references)
//IL_0248: Expected O, but got Unknown
if (Chainloader.PluginInfos.TryGetValue("glorfindel.etg.itemtips", out var _) && TranslateTextsOfItemTipsMod.Value)
{
itemTipsModuleType = AccessTools.TypeByName("ItemTipsMod.ItemTipsModule");
MethodInfo methodInfo = AccessTools.Method(itemTipsModuleType, "ShowTip", (Type[])null, (Type[])null);
MethodInfo methodInfo2 = AccessTools.Method(typeof(AutoTranslatePatches.ShowTipPatchClass), "ShowTipPostfix", (Type[])null, (Type[])null);
harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
MethodInfo methodInfo3 = AccessTools.Method(itemTipsModuleType, "LoadFont", (Type[])null, (Type[])null);
MethodInfo methodInfo4 = AccessTools.Method(typeof(AutoTranslatePatches.LoadFontPatchClass), "LoadFontPrefix", (Type[])null, (Type[])null);
harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(methodInfo4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
}
if ((Object)(object)fontManager.dfFontBase != (Object)null && fontManager.dfFontBase is dfDynamicFont)
{
Type typeFromHandle = typeof(DynamicFontRenderer);
MethodInfo methodInfo5 = AccessTools.Method(typeFromHandle, "tokenize", (Type[])null, (Type[])null);
MethodInfo methodInfo6 = AccessTools.Method(typeof(AutoTranslatePatches.DynamicFontRendererTokenizePatchClass), "TokenizePrefix", (Type[])null, (Type[])null);
harmony.Patch((MethodBase)methodInfo5, new HarmonyMethod(methodInfo6), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
MethodInfo methodInfo7 = AccessTools.Method(typeFromHandle, "calculateLinebreaks", (Type[])null, (Type[])null);
MethodInfo methodInfo8 = AccessTools.Method(typeof(AutoTranslatePatches.DynamicFontRendererCalculateLinebreaksPatchClass), "CalculateLinebreaksPatch", (Type[])null, (Type[])null);
harmony.Patch((MethodBase)methodInfo7, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(methodInfo8));
}
else if ((Object)(object)fontManager.dfFontBase != (Object)null && fontManager.dfFontBase is dfFont)
{
Type typeFromHandle2 = typeof(BitmappedFontRenderer);
MethodInfo methodInfo9 = AccessTools.Method(typeFromHandle2, "tokenize", (Type[])null, (Type[])null);
MethodInfo methodInfo10 = AccessTools.Method(typeof(AutoTranslatePatches.BitmappedFontRendererTokenizePatchClass), "TokenizePrefix", (Type[])null, (Type[])null);
harmony.Patch((MethodBase)methodInfo9, new HarmonyMethod(methodInfo10), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
MethodInfo methodInfo11 = AccessTools.Method(typeFromHandle2, "calculateLinebreaks", (Type[])null, (Type[])null);
MethodInfo methodInfo12 = AccessTools.Method(typeof(AutoTranslatePatches.BitmappedFontRendererCalculateLinebreaksPatchClass), "CalculateLinebreaksPatch", (Type[])null, (Type[])null);
harmony.Patch((MethodBase)methodInfo11, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(methodInfo12));
}
}
}
public static class AutoTranslatePatches
{
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
public class dfLabelTextPatchClass
{
[HarmonyPostfix]
public static void dfLabelTextPostfix(dfLabel __instance)
{
dfFontBase dfFontBase = AutoTranslateModule.instance.fontManager.dfFontBase;
if ((Object)(object)dfFontBase != (Object)null && (Object)(object)__instance.Font != (Object)(object)dfFontBase)
{
__instance.Font = dfFontBase;
dfFont val = (dfFont)(object)((dfFontBase is dfFont) ? dfFontBase : null);
if (val != null)
{
__instance.Atlas = val.Atlas;
}
}
AutoTranslateModule.instance.translateManager.AddTranslationRequest(__instance.text, __instance);
}
}
[HarmonyPatch(typeof(dfLabel), "OnLocalize")]
public class dfLabelOnLocalizePatchClass
{
[HarmonyPostfix]
public static void dfLabelOnLocalizePostfix(dfLabel __instance)
{
dfFontBase dfFontBase = AutoTranslateModule.instance.fontManager.dfFontBase;
if ((Object)(object)dfFontBase != (Object)null && (Object)(object)__instance.Font != (Object)(object)dfFontBase)
{
__instance.Font = dfFontBase;
dfFont val = (dfFont)(object)((dfFontBase is dfFont) ? dfFontBase : null);
if (val != null)
{
__instance.Atlas = val.Atlas;
}
}
AutoTranslateModule.instance.translateManager.AddTranslationRequest(__instance.text, __instance);
}
}
[HarmonyPatch(typeof(dfLabel), "ModifyLocalizedText")]
public class dfLabelModifyLocalizedTextPatchClass
{
[HarmonyPostfix]
public static void dfLabelModifyLocalizedTextPostfix(dfLabel __instance)
{
dfFontBase dfFontBase = AutoTranslateModule.instance.fontManager.dfFontBase;
if ((Object)(object)dfFontBase != (Object)null && (Object)(object)__instance.Font != (Object)(object)dfFontBase)
{
__instance.Font = dfFontBase;
dfFont val = (dfFont)(object)((dfFontBase is dfFont) ? dfFontBase : null);
if (val != null)
{
__instance.Atlas = val.Atlas;
}
}
AutoTranslateModule.instance.translateManager.AddTranslationRequest(__instance.text, __instance);
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
public class dfButtonTextPatchClass
{
[HarmonyPostfix]
public static void dfButtonTextPostfix(dfButton __instance)
{
dfFontBase dfFontBase = AutoTranslateModule.instance.fontManager.dfFontBase;
if ((Object)(object)dfFontBase != (Object)null && (Object)(object)__instance.Font != (Object)(object)dfFontBase)
{
__instance.Font = dfFontBase;
dfFont val = (dfFont)(object)((dfFontBase is dfFont) ? dfFontBase : null);
if (val != null)
{
((dfInteractiveBase)__instance).Atlas = val.Atlas;
}
}
AutoTranslateModule.instance.translateManager.AddTranslationRequest(__instance.text, __instance);
}
}
[HarmonyPatch(typeof(dfButton), "OnLocalize")]
public class dfButtonOnLocalizePatchClass
{
[HarmonyPostfix]
public static void dfButtonOnLocalizePostfix(dfButton __instance)
{
dfFontBase dfFontBase = AutoTranslateModule.instance.fontManager.dfFontBase;
if ((Object)(object)dfFontBase != (Object)null && (Object)(object)__instance.Font != (Object)(object)dfFontBase)
{
__instance.Font = dfFontBase;
dfFont val = (dfFont)(object)((dfFontBase is dfFont) ? dfFontBase : null);
if (val != null)
{
((dfInteractiveBase)__instance).Atlas = val.Atlas;
}
}
AutoTranslateModule.instance.translateManager.AddTranslationRequest(__instance.text, __instance);
}
}
[HarmonyPatch(typeof(dfButton), "ModifyLocalizedText")]
public class dfButtonModifyLocalizedTextPatchClass
{
[HarmonyPostfix]
public static void dfButtonModifyLocalizedTextPostfix(dfButton __instance)
{
dfFontBase dfFontBase = AutoTranslateModule.instance.fontManager.dfFontBase;
if ((Object)(object)dfFontBase != (Object)null && (Object)(object)__instance.Font != (Object)(object)dfFontBase)
{
__instance.Font = dfFontBase;
dfFont val = (dfFont)(object)((dfFontBase is dfFont) ? dfFontBase : null);
if (val != null)
{
((dfInteractiveBase)__instance).Atlas = val.Atlas;
}
}
AutoTranslateModule.instance.translateManager.AddTranslationRequest(__instance.text, __instance);
}
}
public class ShowTipPatchClass
{
private static SLabel sLabel;
public static void ShowTipPostfix(object __instance)
{
if (sLabel == null)
{
Type itemTipsModuleType = AutoTranslateModule.instance.itemTipsModuleType;
object? obj = itemTipsModuleType.GetField("_infoLabel", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(__instance);
sLabel = (SLabel)((obj is SLabel) ? obj : null);
}
string text = sLabel?.Text;
if (text != null)
{
AutoTranslateModule.instance.translateManager.AddTranslationRequest(text, sLabel);
}
}
}
public class LoadFontPatchClass
{
public static bool LoadFontPrefix(ref Font __result)
{
dfFontBase dfFontBase = AutoTranslateModule.instance.fontManager.dfFontBase;
if ((Object)(object)dfFontBase != (Object)null)
{
dfDynamicFont val = (dfDynamicFont)(object)((dfFontBase is dfDynamicFont) ? dfFontBase : null);
if (val != null)
{
__result = val.baseFont;
return false;
}
}
if ((Object)(object)dfFontBase != (Object)null)
{
dfFont val2 = (dfFont)(object)((dfFontBase is dfFont) ? dfFontBase : null);
if (val2 != null)
{
__result = FontConverter.GetFontFromdfFont(val2, 2);
return false;
}
}
dfFont gameFont = FontManager.GetGameFont();
if ((Object)(object)gameFont != (Object)null)
{
__result = FontConverter.GetFontFromdfFont(gameFont, 2);
return false;
}
return true;
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
public class tk2dTextMeshTextPatchClass
{
[HarmonyPostfix]
public static void tk2dTextMeshTextPostfix(tk2dTextMesh __instance)
{
tk2dFontData tk2dFont = AutoTranslateModule.instance.fontManager.tk2dFont;
if ((Object)(object)tk2dFont != (Object)null && (Object)(object)__instance.font != (Object)(object)tk2dFont)
{
FontManager.SetTextMeshFont(__instance, tk2dFont);
}
if (__instance.data != null && __instance.data.text != null)
{
AutoTranslateModule.instance.translateManager.AddTranslationRequest(__instance.data.text, __instance);
}
}
}
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
public class tk2dTextMeshFontPatchClass
{
[HarmonyPrefix]
public static bool tk2dTextMeshFontPrefix(tk2dTextMesh __instance)
{
tk2dFontData tk2dFont = AutoTranslateModule.instance.fontManager.tk2dFont;
if ((Object)(object)tk2dFont != (Object)null && (Object)(object)__instance.font != (Object)(object)tk2dFont)
{
FontManager.SetTextMeshFont(__instance, tk2dFont);
}
return false;
}
}
[HarmonyPatch(typeof(TextBoxManager), "SetText")]
public class SetTextPatchClass
{
[HarmonyPrefix]
public static bool SetTextPrefix(ref bool instant)
{
instant = true;
return true;
}
}
public class DynamicFontRendererTokenizePatchClass
{
public static bool TokenizePrefix(DynamicFontRenderer __instance, string text)
{
try
{
if (__instance.tokens != null)
{
if ((object)__instance.tokens[0].Source == text)
{
return false;
}
__instance.tokens.ReleaseItems();
__instance.tokens.Release();
}
__instance.tokens = AutoTranslateModule.instance.fontManager?.Tokenize(text);
for (int i = 0; i < __instance.tokens.Count; i++)
{
__instance.calculateTokenRenderSize(__instance.tokens[i]);
}
}
finally
{
}
return false;
}
}
public class DynamicFontRendererCalculateLinebreaksPatchClass
{
[HarmonyILManipulator]
public static void CalculateLinebreaksPatch(ILContext ctx)
{
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_0013: Expected O, but got Unknown
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
ILCursor crs = new ILCursor(ctx);
if (TheNthTime(() => crs.TryGotoNext((MoveType)0, new Func<Instruction, bool>[1]
{
(Instruction x) => ILPatternMatchingExt.MatchLdcI4(x, 0)
}), 8))
{
crs.Emit(OpCodes.Ldloca_S, (byte)3);
crs.EmitCall<DynamicFontRendererCalculateLinebreaksPatchClass>("CalculateLinebreaksPatchCall_1");
ILCursor obj = crs;
int index = obj.Index;
obj.Index = index + 1;
}
if (crs.TryGotoNext((MoveType)0, new Func<Instruction, bool>[1]
{
(Instruction x) => ILPatternMatchingExt.MatchLdcI4(x, 0)
}))
{
crs.Emit(OpCodes.Ldloca_S, (byte)3);
crs.EmitCall<DynamicFontRendererCalculateLinebreaksPatchClass>("CalculateLinebreaksPatchCall_1");
}
if (crs.TryGotoNext((MoveType)0, new Func<Instruction, bool>[1]
{
(Instruction x) => ILPatternMatchingExt.MatchLdcI4(x, 2)
}))
{
crs.EmitCall<DynamicFontRendererCalculateLinebreaksPatchClass>("CalculateLinebreaksPatchCall_2");
}
}
private static void CalculateLinebreaksPatchCall_1(ref int orig)
{
orig--;
}
private static int CalculateLinebreaksPatchCall_2(int orig)
{
return 2;
}
}
public class BitmappedFontRendererTokenizePatchClass
{
public static bool TokenizePrefix(BitmappedFontRenderer __instance, string text, ref dfList<dfMarkupToken> __result)
{
try
{
if (__instance.tokens != null)
{
if ((object)__instance.tokens[0].Source == text)
{
__result = __instance.tokens;
return false;
}
__instance.tokens.ReleaseItems();
__instance.tokens.Release();
}
__instance.tokens = AutoTranslateModule.instance.fontManager?.Tokenize(text);
for (int i = 0; i < __instance.tokens.Count; i++)
{
__instance.calculateTokenRenderSize(__instance.tokens[i]);
}
__result = __instance.tokens;
}
finally
{
}
return false;
}
}
public class BitmappedFontRendererCalculateLinebreaksPatchClass
{
[HarmonyILManipulator]
public static void CalculateLinebreaksPatch(ILContext ctx)
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Expected O, but got Unknown
ILCursor val = new ILCursor(ctx);
if (val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[1]
{
(Instruction x) => ILPatternMatchingExt.MatchLdcI4(x, 7)
}))
{
val.EmitCall<BitmappedFontRendererCalculateLinebreaksPatchClass>("CalculateLinebreaksPatchCall_1");
}
if (val.TryGotoNext((MoveType)0, new Func<Instruction, bool>[1]
{
(Instruction x) => ILPatternMatchingExt.MatchLdcI4(x, 2)
}))
{
val.EmitCall<BitmappedFontRendererCalculateLinebreaksPatchClass>("CalculateLinebreaksPatchCall_2");
}
}
private static int CalculateLinebreaksPatchCall_1(int orig)
{
return 7;
}
private static int CalculateLinebreaksPatchCall_2(int orig)
{
return 2;
}
}
[HarmonyPatch(typeof(UINotificationController), "DoNotificationInternal")]
public class CheckLanguageFontsPatchClass
{
[HarmonyPostfix]
public static void DoNotificationInternalPostfix(UINotificationController __instance)
{
__instance.CenterLabel.TextScale = 2f;
}
}
[HarmonyPatch(typeof(tk2dTextMesh), "CheckFontsForLanguage")]
public class tk2dTextMeshCheckFontsForLanguagePatchClass
{
[HarmonyPrefix]
public static bool CheckFontsForLanguagePrefix()
{
if ((Object)(object)AutoTranslateModule.instance.fontManager.tk2dFont != (Object)null)
{
return false;
}
return true;
}
}
[HarmonyPatch(typeof(dfLabel), "CheckFontsForLanguage")]
public class dfLabelCheckFontsForLanguagePatchClass
{
[HarmonyPrefix]
public static bool CheckFontsForLanguagePrefix()
{
if ((Object)(object)AutoTranslateModule.instance.fontManager.tk2dFont != (Object)null)
{
return false;
}
return true;
}
}
[HarmonyPatch(typeof(dfButton), "CheckFontsForLanguage")]
public class dfButtonCheckFontsForLanguagePatchClass
{
[HarmonyPrefix]
public static bool CheckFontsForLanguagePrefix()
{
if ((Object)(object)AutoTranslateModule.instance.fontManager.tk2dFont != (Object)null)
{
return false;
}
return true;
}
}
public static void EmitCall<T>(this ILCursor iLCursor, string methodName, Type[] parameters = null, Type[] generics = null)
{
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
MethodInfo methodInfo = AccessTools.Method(typeof(T), methodName, parameters, generics);
iLCursor.Emit(OpCodes.Call, (MethodBase)methodInfo);
}
public static bool TheNthTime(this Func<bool> predict, int n = 1)
{
for (int i = 0; i < n; i++)
{
if (!predict())
{
return false;
}
}
return true;
}
}
public class AzureTranslationService : ITranslationService
{
private TranslationManagerConfig config;
private string endpoint = "https://api.cognitive.microsofttranslator.com";
private string action = "/translate";
private string version = "3.0";
public AzureTranslationService(TranslationManagerConfig config)
{
this.config = config;
}
private string[] ParseResponse(string responseJson)
{
//IL_000d: Expected O, but got Unknown
JArray val;
try
{
val = JArray.Parse(responseJson);
}
catch (JsonReaderException val2)
{
JsonReaderException innerException = val2;
throw new InvalidOperationException("响应的JSON格式无效 The JSON format of the response is invalid: ", (Exception?)(object)innerException);
}
List<string> list = new List<string>();
foreach (JToken item in val)
{
JToken obj = item[(object)"translations"];
JArray val3 = (JArray)(object)((obj is JArray) ? obj : null);
if (val3 == null)
{
continue;
}
foreach (JToken item2 in val3)
{
list.Add(((object)item2[(object)"text"]).ToString());
}
}
if (list.Count == 0)
{
throw new InvalidOperationException("翻译结果为空!The translation result is empty!");
}
return list.ToArray();
}
public static List<string> PreprocessText(string[] inputTexts)
{
return inputTexts.Select((string text) => text.Replace("\r", "")).ToList();
}
public IEnumerator StartTranslation(string[] texts, Action<string[]> callback)
{
string[] preprocessedTexts = PreprocessText(texts).ToArray();
var payload = preprocessedTexts.Select((string text) => new
{
Text = text
}).ToArray();
string payloadJson = JsonConvert.SerializeObject((object)payload);
string subscriptionKey = config.AzureSubscriptionKey;
string region = config.AzureRegion;
string requestUrl = endpoint + action + "?api-version=" + version + "&to=" + config.AzureTargetLanguage;
if (config.AzureSourceLanguage != string.Empty)
{
requestUrl = requestUrl + "&from=" + config.AzureSourceLanguage;
}
int retryCount = 0;
bool needRetry = false;
while (true)
{
if (needRetry)
{
if (retryCount >= config.MaxRetryCount)
{
break;
}
retryCount++;
Debug.Log((object)$"正在重试。。。尝试第 {retryCount} 次。Retrying... Attempt time {retryCount}.");
needRetry = false;
yield return (object)new WaitForSecondsRealtime(2f);
}
UnityWebRequest request = new UnityWebRequest(requestUrl, "POST");
try
{
byte[] bodyRaw = Encoding.UTF8.GetBytes(payloadJson);
request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bodyRaw);
request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
request.SetRequestHeader("Ocp-Apim-Subscription-Key", subscriptionKey);
request.SetRequestHeader("Ocp-Apim-Subscription-Region", region);
yield return request.SendWebRequest();
if (request.isNetworkError || request.isHttpError)
{
Debug.LogError((object)("请求失败 Request failed: " + request.error));
needRetry = true;
continue;
}
string responseJson = request.downloadHandler.text;
string[] translatedTexts;
try
{
translatedTexts = ParseResponse(responseJson);
}
catch (Exception ex2)
{
Exception ex = ex2;
Debug.LogError((object)("解析翻译结果失败 Failed to parse translation result:" + ex.Message));
Debug.LogError((object)("响应JSON Response JSON:\n" + responseJson));
needRetry = true;
goto end_IL_0277;
}
if (translatedTexts != null)
{
callback?.Invoke(translatedTexts);
yield break;
}
Debug.LogError((object)"翻译失败,未获得翻译结果!Translation failed, no translation result obtained!");
needRetry = true;
yield break;
end_IL_0277:;
}
finally
{
((IDisposable)request)?.Dispose();
}
}
Debug.LogError((object)$"多次尝试失败。已重试 {config.MaxRetryCount} 次。翻译中止!Multiple attempts failed. Retried {config.MaxRetryCount} times. translation aborted!");
callback?.Invoke(null);
}
}
public class BaiduTranslationService : ITranslationService
{
private TranslationManagerConfig config;
private string apiUrl = "https://fanyi-api.baidu.com/api/trans/vip/translate";
public BaiduTranslationService(TranslationManagerConfig config)
{
this.config = config;
}
private string[] ParseResponse(string responseJson)
{
//IL_000d: Expected O, but got Unknown
JObject val;
try
{
val = JObject.Parse(responseJson);
}
catch (JsonReaderException val2)
{
JsonReaderException innerException = val2;
throw new InvalidOperationException("响应的JSON格式无效 The JSON format of the response is invalid: ", (Exception?)(object)innerException);
}
if (val.ContainsKey("error_code"))
{
string text = ((object)val["error_code"])?.ToString();
string text2 = ((object)val["error_msg"])?.ToString();
throw new InvalidOperationException("API请求失败 API request failed: " + text + " - " + text2);
}
if (!val.ContainsKey("trans_result"))
{
throw new InvalidOperationException("响应JSON中缺少trans_desult字段!The 'trans_desult' field is missing in the response JSON!");
}
JToken val3 = val["trans_result"];
if (val3 == null || !((IEnumerable<JToken>)val3).Any())
{
throw new InvalidOperationException("翻译结果为空!The translation result is empty!");
}
return ((IEnumerable<JToken>)val3).Select((JToken result) => ((object)result[(object)"dst"])?.ToString() ?? string.Empty).ToArray();
}
public static List<string> PreprocessText(string[] inputTexts)
{
return inputTexts.Select((string text) => text.Replace("\n", "\\n").Replace("\r", "")).ToList();
}
public IEnumerator StartTranslation(string[] texts, Action<string[]> callback)
{
string salt = Guid.NewGuid().ToString();
string[] preprocessedTexts = PreprocessText(texts).ToArray();
string sign = GenerateSign(config.BaiduAppId, string.Join("\n", preprocessedTexts), salt, config.BaiduSecretKey);
string url = apiUrl + "?q=" + Uri.EscapeDataString(string.Join("\n", preprocessedTexts)) + "&from=" + config.BaiduSourceLanguage + "&to=" + config.BaiduTargetLanguage + "&appid=" + config.BaiduAppId + "&salt=" + salt + "&sign=" + sign;
bool needRetry = false;
int retryCount = 0;
while (true)
{
if (needRetry)
{
if (retryCount >= config.MaxRetryCount)
{
break;
}
retryCount++;
Debug.Log((object)$"正在重试。。。尝试第 {retryCount} 次。Retrying... Attempt time {retryCount}.");
needRetry = false;
yield return (object)new WaitForSecondsRealtime(2f);
}
UnityWebRequest request = UnityWebRequest.Get(url);
try
{
yield return request.SendWebRequest();
if (request.isNetworkError || request.isHttpError)
{
Debug.LogError((object)("请求失败 Request failed: " + request.error));
needRetry = true;
continue;
}
string responseJson = request.downloadHandler.text;
string[] translatedTexts;
try
{
translatedTexts = ParseResponse(responseJson);
}
catch (Exception ex2)
{
Exception ex = ex2;
Debug.LogError((object)("解析翻译结果失败 Failed to parse translation result:" + ex.Message));
Debug.LogError((object)("响应JSON Response JSON:\n" + responseJson));
needRetry = true;
goto end_IL_0275;
}
if (translatedTexts != null)
{
callback?.Invoke(translatedTexts);
yield break;
}
Debug.LogError((object)"翻译失败,未获得翻译结果!Translation failed, no translation result obtained!");
needRetry = true;
yield break;
end_IL_0275:;
}
finally
{
((IDisposable)request)?.Dispose();
}
}
Debug.LogError((object)$"多次尝试失败。已重试 {config.MaxRetryCount} 次。翻译中止!Multiple attempts failed. Retried {config.MaxRetryCount} times. translation aborted!");
callback?.Invoke(null);
}
private string GenerateSign(string appId, string query, string salt, string secretKey)
{
string s = appId + query + salt + secretKey;
using MD5 mD = MD5.Create();
byte[] array = mD.ComputeHash(Encoding.UTF8.GetBytes(s));
StringBuilder stringBuilder = new StringBuilder();
byte[] array2 = array;
foreach (byte b in array2)
{
stringBuilder.Append(b.ToString("x2"));
}
return stringBuilder.ToString();
}
}
public interface ITranslationService
{
IEnumerator StartTranslation(string[] texts, Action<string[]> callback);
}
public class LRUCache<TKey, TValue>
{
private class CacheItem
{
public TKey Key;
public TValue Value;
public CacheItem(TKey key, TValue value)
{
Key = key;
Value = value;
}
}
private readonly int _capacity;
private readonly Dictionary<TKey, LinkedListNode<CacheItem>> _cache;
private readonly LinkedList<CacheItem> _order;
public LRUCache(int capacity)
{
if (capacity <= 0)
{
throw new ArgumentException("容量必须大于0。Capacity must be greater than zero.");
}
_capacity = capacity;
_cache = new Dictionary<TKey, LinkedListNode<CacheItem>>(capacity);
_order = new LinkedList<CacheItem>();
}
public TValue Get(TKey key)
{
if (_cache.TryGetValue(key, out var value))
{
_order.Remove(value);
_order.AddFirst(value);
return value.Value.Value;
}
return default(TValue);
}
public bool TryGetValue(TKey key, out TValue value)
{
if (_cache.TryGetValue(key, out var value2))
{
_order.Remove(value2);
_order.AddFirst(value2);
value = value2.Value.Value;
return true;
}
value = default(TValue);
return false;
}
public void Set(TKey key, TValue value)
{
if (_cache.TryGetValue(key, out var value2))
{
_order.Remove(value2);
}
else if (_cache.Count >= _capacity)
{
LinkedListNode<CacheItem> last = _order.Last;
_cache.Remove(last.Value.Key);
_order.RemoveLast();
}
LinkedListNode<CacheItem> linkedListNode = new LinkedListNode<CacheItem>(new CacheItem(key, value));
_order.AddFirst(linkedListNode);
_cache[key] = linkedListNode;
}
public bool ContainsKey(TKey key)
{
return _cache.ContainsKey(key);
}
}
public class TencentTranslationService : ITranslationService
{
private TranslationManagerConfig config;
private string endpoint = "tmt.tencentcloudapi.com";
private string action = "TextTranslateBatch";
private string version = "2018-03-21";
public TencentTranslationService(TranslationManagerConfig config)
{
this.config = config;
}
private string[] ParseResponse(string responseJson)
{
//IL_000d: Expected O, but got Unknown
JObject val;
try
{
val = JObject.Parse(responseJson);
}
catch (JsonReaderException val2)
{
JsonReaderException innerException = val2;
throw new InvalidOperationException("响应的JSON格式无效 The JSON format of the response is invalid: ", (Exception?)(object)innerException);
}
if (val["Response"] == null)
{
throw new InvalidOperationException("响应中缺少Response字段!The 'Response' field is missing from the response!");
}
JToken val3 = val["Response"][(object)"TargetTextList"];
if (val3 == null)
{
throw new InvalidOperationException("响应中缺少TargetTextList字段!The 'TargetTextList' field is missing from the response!");
}
string[] array = val3.ToObject<string[]>();
if (array == null || array.Length == 0)
{
throw new InvalidOperationException("翻译结果为空!The translation result is empty!");
}
return array;
}
public static List<string> PreprocessText(string[] inputTexts)
{
return inputTexts.Select((string text) => text.Replace("\r", "")).ToList();
}
public IEnumerator StartTranslation(string[] texts, Action<string[]> callback)
{
string[] preprocessedTexts = PreprocessText(texts).ToArray();
long timestamp = GetUnixTimeSeconds();
string date = DateTime.UtcNow.ToString("yyyy-MM-dd");
var payload = new
{
SourceTextList = preprocessedTexts,
Source = config.TencentSourceLanguage,
Target = config.TencentTargetLanguage,
ProjectId = 0
};
string payloadJson = JsonConvert.SerializeObject((object)payload);
string stringToSign = string.Format(arg2: ComputeSHA256("POST\n/\n\ncontent-type:application/json\nhost:" + endpoint + "\n\ncontent-type;host\n" + ComputeSHA256(payloadJson)), format: "TC3-HMAC-SHA256\n{0}\n{1}/tmt/tc3_request\n{2}", arg0: timestamp, arg1: date);
byte[] signingKey = GetSignatureKey(config.TencentSecretKey, date, "tmt", "tc3_request");
string signature = ConvertToHexString(ComputeHMACSHA256(stringToSign, signingKey));
string authorization = "TC3-HMAC-SHA256 Credential=" + config.TencentSecretId + "/" + date + "/tmt/tc3_request, SignedHeaders=content-type;host, Signature=" + signature;
int retryCount = 0;
bool needRetry = false;
while (true)
{
if (needRetry)
{
if (retryCount >= config.MaxRetryCount)
{
break;
}
retryCount++;
Debug.Log((object)$"正在重试。。。尝试第 {retryCount} 次。Retrying... Attempt time {retryCount}.");
needRetry = false;
yield return (object)new WaitForSecondsRealtime(2f);
}
UnityWebRequest request = new UnityWebRequest("https://" + endpoint, "POST");
try
{
byte[] bodyRaw = Encoding.UTF8.GetBytes(payloadJson);
request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bodyRaw);
request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
request.SetRequestHeader("Authorization", authorization);
request.SetRequestHeader("X-TC-Action", action);
request.SetRequestHeader("X-TC-Timestamp", timestamp.ToString());
request.SetRequestHeader("X-TC-Version", version);
request.SetRequestHeader("X-TC-Region", config.TencentRegion);
yield return request.SendWebRequest();
if (request.isNetworkError || request.isHttpError)
{
Debug.LogError((object)("请求失败 Request failed: " + request.error));
needRetry = true;
continue;
}
string responseJson = request.downloadHandler.text;
string[] translatedTexts;
try
{
translatedTexts = ParseResponse(responseJson);
}
catch (Exception ex2)
{
Exception ex = ex2;
Debug.LogError((object)("解析翻译结果失败 Failed to parse translation result:" + ex.Message));
Debug.LogError((object)("响应JSON Response JSON:\n" + responseJson));
needRetry = true;
goto end_IL_02e8;
}
if (translatedTexts != null)
{
callback?.Invoke(translatedTexts);
yield break;
}
Debug.LogError((object)"翻译失败,未获得翻译结果!Translation failed, no translation result obtained!");
needRetry = true;
yield break;
end_IL_02e8:;
}
finally
{
((IDisposable)request)?.Dispose();
}
}
Debug.LogError((object)$"多次尝试失败。已重试 {config.MaxRetryCount} 次。翻译中止!Multiple attempts failed. Retried {config.MaxRetryCount} times. translation aborted!");
callback?.Invoke(null);
}
private string ComputeSHA256(string rawData)
{
using SHA256 sHA = SHA256.Create();
byte[] bytes = sHA.ComputeHash(Encoding.UTF8.GetBytes(rawData));
return ConvertToHexString(bytes);
}
private byte[] ComputeHMACSHA256(string data, byte[] key)
{
using HMACSHA256 hMACSHA = new HMACSHA256(key);
return hMACSHA.ComputeHash(Encoding.UTF8.GetBytes(data));
}
private byte[] GetSignatureKey(string secretKey, string date, string service, string request)
{
byte[] key = ComputeHMACSHA256(date, Encoding.UTF8.GetBytes("TC3" + secretKey));
byte[] key2 = ComputeHMACSHA256(service, key);
return ComputeHMACSHA256(request, key2);
}
private string ConvertToHexString(byte[] bytes)
{
StringBuilder stringBuilder = new StringBuilder();
foreach (byte b in bytes)
{
stringBuilder.Append(b.ToString("x2"));
}
return stringBuilder.ToString();
}
private long GetUnixTimeSeconds()
{
return (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
}
}
public class FontManager
{
public string regexForTokenizer;
public AssetBundle assetBundle;
public dfFontBase dfFontBase;
public tk2dFontData tk2dFont;
public AutoTranslateModule.OverrideFontType overrideFont;
public bool customFontLoaded = false;
public static string defaultRegexForTokenizer = "(?<StartTag>\\[(?<TagName>(color|sprite))\\s*(?:\"(?<AttributeValue>[^\"]*)\")?\\])|(?<EndTag>\\[/\\k<TagName>\\])|(?<Newline>\\r?\\n)|(?<Whitespace>\\s+)|(?<Text>[a-zA-Z0-9]+|.)";
public FontManager(AutoTranslateModule.OverrideFontType fontType, string assetBundleName, string dfFontName, string tk2dFontName, string regex)
{
regexForTokenizer = ((regex == string.Empty) ? defaultRegexForTokenizer : regex);
overrideFont = fontType;
switch (fontType)
{
case AutoTranslateModule.OverrideFontType.None:
case AutoTranslateModule.OverrideFontType.English:
return;
case AutoTranslateModule.OverrideFontType.Custom:
if (!(assetBundleName == string.Empty) && (!(dfFontName == string.Empty) || !(tk2dFontName == string.Empty)))
{
string filePath = Path.Combine(ETGMod.FolderPath((BaseUnityPlugin)(object)AutoTranslateModule.instance), assetBundleName);
try
{
assetBundle = AssetBundleLoader.LoadAssetBundle(filePath);
dfFontBase = assetBundle.LoadAsset<GameObject>(dfFontName).GetComponent<dfFontBase>();
tk2dFont = assetBundle.LoadAsset<GameObject>(tk2dFontName).GetComponent<tk2dFont>().data;
}
catch
{
}
customFontLoaded = true;
}
return;
}
if (fontType != AutoTranslateModule.OverrideFontType.Polish)
{
switch (fontType)
{
case AutoTranslateModule.OverrideFontType.Chinese:
{
ref dfFontBase reference7 = ref dfFontBase;
Object obj8 = ResourceCache.Acquire("Alternate Fonts/SimSun12_DF");
reference7 = (dfFontBase)(object)((GameObject)((obj8 is GameObject) ? obj8 : null)).GetComponent<dfFont>();
ref tk2dFontData reference8 = ref tk2dFont;
Object obj9 = ResourceCache.Acquire("Alternate Fonts/SimSun12_TK2D");
reference8 = ((GameObject)((obj9 is GameObject) ? obj9 : null)).GetComponent<tk2dFont>().data;
break;
}
case AutoTranslateModule.OverrideFontType.Japanese:
{
ref dfFontBase reference5 = ref dfFontBase;
Object obj6 = ResourceCache.Acquire("Alternate Fonts/JackeyFont12_DF");
reference5 = (dfFontBase)(object)((GameObject)((obj6 is GameObject) ? obj6 : null)).GetComponent<dfFont>();
ref tk2dFontData reference6 = ref tk2dFont;
Object obj7 = ResourceCache.Acquire("Alternate Fonts/JackeyFont_TK2D");
reference6 = ((GameObject)((obj7 is GameObject) ? obj7 : null)).GetComponent<tk2dFont>().data;
break;
}
case AutoTranslateModule.OverrideFontType.Korean:
{
ref dfFontBase reference3 = ref dfFontBase;
Object obj4 = ResourceCache.Acquire("Alternate Fonts/NanumGothic16_DF");
reference3 = (dfFontBase)(object)((GameObject)((obj4 is GameObject) ? obj4 : null)).GetComponent<dfFont>();
ref tk2dFontData reference4 = ref tk2dFont;
Object obj5 = ResourceCache.Acquire("Alternate Fonts/NanumGothic16TK2D");
reference4 = ((GameObject)((obj5 is GameObject) ? obj5 : null)).GetComponent<tk2dFont>().data;
break;
}
case AutoTranslateModule.OverrideFontType.Russian:
{
ref dfFontBase reference = ref dfFontBase;
Object obj2 = ResourceCache.Acquire("Alternate Fonts/PixelaCYR_15_DF");
reference = (dfFontBase)(object)((GameObject)((obj2 is GameObject) ? obj2 : null)).GetComponent<dfFont>();
ref tk2dFontData reference2 = ref tk2dFont;
Object obj3 = ResourceCache.Acquire("Alternate Fonts/PixelaCYR_15_TK2D");
reference2 = ((GameObject)((obj3 is GameObject) ? obj3 : null)).GetComponent<tk2dFont>().data;
break;
}
}
}
}
private int estimateTokenCount(string source)
{
if (string.IsNullOrEmpty(source))
{
return 0;
}
return source.Length;
}
public dfList<dfMarkupToken> Tokenize(string source)
{
dfList<dfMarkupToken> val = dfList<dfMarkupToken>.Obtain();
val.EnsureCapacity(estimateTokenCount(source));
val.AutoReleaseItems = true;
Regex regex = new Regex(regexForTokenizer, RegexOptions.Multiline | RegexOptions.Compiled);
MatchCollection matchCollection = regex.Matches(source);
foreach (Match item in matchCollection)
{
if (item.Groups["StartTag"].Success)
{
Capture capture = item.Groups["TagName"];
Capture capture2 = item.Groups["AttributeValue"];
dfMarkupToken val2 = dfMarkupToken.Obtain(source, (dfMarkupTokenType)4, capture.Index, capture.Index + capture.Length - 1);
if (capture2.Value != string.Empty)
{
dfMarkupToken val3 = dfMarkupToken.Obtain(source, (dfMarkupTokenType)1, capture2.Index, capture2.Index + capture2.Length - 1);
val2.AddAttribute(val3, val3);
}
val.Add(val2);
}
else if (item.Groups["EndTag"].Success)
{
val.Add(dfMarkupToken.Obtain(source, (dfMarkupTokenType)5, item.Index, item.Index + item.Length - 1));
}
else if (item.Groups["Text"].Success)
{
val.Add(dfMarkupToken.Obtain(source, (dfMarkupTokenType)1, item.Index, item.Index + item.Length - 1));
}
else if (item.Groups["Whitespace"].Success)
{
val.Add(dfMarkupToken.Obtain(source, (dfMarkupTokenType)2, item.Index, item.Index + item.Length - 1));
}
else if (item.Groups["Newline"].Success)
{
val.Add(dfMarkupToken.Obtain(source, (dfMarkupTokenType)3, item.Index, item.Index + item.Length - 1));
}
}
return val;
}
internal static dfFont GetGameFont()
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0017: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Expected I4, but got Unknown
dfFont result = null;
GungeonSupportedLanguages currentLanguage = GameManager.Options.CurrentLanguage;
GungeonSupportedLanguages val = currentLanguage;
GungeonSupportedLanguages val2 = val;
if ((int)val2 != 0)
{
switch (val2 - 7)
{
case 4:
{
Object obj4 = ResourceCache.Acquire("Alternate Fonts/SimSun12_DF");
result = ((GameObject)((obj4 is GameObject) ? obj4 : null)).GetComponent<dfFont>();
break;
}
case 0:
{
Object obj3 = ResourceCache.Acquire("Alternate Fonts/JackeyFont12_DF");
result = ((GameObject)((obj3 is GameObject) ? obj3 : null)).GetComponent<dfFont>();
break;
}
case 1:
{
Object obj2 = ResourceCache.Acquire("Alternate Fonts/NanumGothic16_DF");
result = ((GameObject)((obj2 is GameObject) ? obj2 : null)).GetComponent<dfFont>();
break;
}
case 2:
{
Object obj = ResourceCache.Acquire("Alternate Fonts/PixelaCYR_15_DF");
result = ((GameObject)((obj is GameObject) ? obj : null)).GetComponent<dfFont>();
break;
}
case 3:
{
dfFontBase defaultFont = GameUIRoot.Instance.Manager.defaultFont;
result = (dfFont)(object)((defaultFont is dfFont) ? defaultFont : null);
break;
}
}
}
else
{
dfFontBase defaultFont2 = GameUIRoot.Instance.Manager.defaultFont;
result = (dfFont)(object)((defaultFont2 is dfFont) ? defaultFont2 : null);
}
return result;
}
internal void InitializeFontAfterGameManager(AutoTranslateModule.OverrideFontType fontType)
{
if (fontType == AutoTranslateModule.OverrideFontType.English || fontType == AutoTranslateModule.OverrideFontType.Polish)
{
ref dfFontBase reference = ref dfFontBase;
dfFontBase defaultFont = GameUIRoot.Instance.Manager.defaultFont;
reference = ((defaultFont is dfFont) ? defaultFont : null);
tk2dFont = GameManager.Instance.DefaultNormalConversationFont;
}
}
internal static void SetTextMeshFont(tk2dTextMesh textMesh, tk2dFontData font)
{
textMesh.UpgradeData();
textMesh.data.font = font;
textMesh._fontInst = textMesh.data.font.inst;
textMesh.SetNeedUpdate((UpdateFlags)1);
textMesh.UpdateMaterial();
}
}
internal class TranslationManager : MonoBehaviour
{
private TranslationManagerConfig config;
private LRUCache<string, string> translationCache;
private bool isProcessingQueue = false;
private Queue<TranslationQueueElement> translationQueue;
private ITranslationService translationService;
private List<string> subTexts;
private Dictionary<string, List<string>> splitMap;
private Dictionary<string, string> translationDictionary;
private HashSet<string> uniqueSubTexts;
private List<string> untranslatedParts;
private List<string> uniqueTexts;
private Dictionary<string, List<object>> textControlMap;
private const int countToNew = 200;
private Regex fullTextRegex;
private Regex eachLineRegex;
private string[] newLineSymbols = new string[3] { "\r\n", "\r", "\n" };
private StringBuilder translatedTextBuilder;
public void Initialize(TranslationManagerConfig config)
{
this.config = config;
switch (config.TranslationAPI)
{
case AutoTranslateModule.TranslationAPIType.Tencent:
translationService = new TencentTranslationService(config);
break;
case AutoTranslateModule.TranslationAPIType.Baidu:
translationService = new BaiduTranslationService(config);
break;
case AutoTranslateModule.TranslationAPIType.Azure:
translationService = new AzureTranslationService(config);
break;
}
translationCache = new LRUCache<string, string>(config.TranslationCacheCapacity);
translationQueue = new Queue<TranslationQueueElement>();
if (config.PresetTranslations != string.Empty)
{
string filePath = Path.Combine(ETGMod.FolderPath((BaseUnityPlugin)(object)AutoTranslateModule.instance), config.PresetTranslations);
ReadAndRestoreTranslationCache(filePath);
}
if (config.RegexForFullTextNeedToTranslate != string.Empty)
{
fullTextRegex = new Regex(config.RegexForFullTextNeedToTranslate, RegexOptions.Singleline);
}
if (config.RegexForEachLineNeedToTranslate != string.Empty)
{
eachLineRegex = new Regex(config.RegexForEachLineNeedToTranslate, RegexOptions.Singleline);
}
subTexts = new List<string>();
splitMap = new Dictionary<string, List<string>>();
translationDictionary = new Dictionary<string, string>();
uniqueSubTexts = new HashSet<string>();
untranslatedParts = new List<string>();
uniqueTexts = new List<string>();
textControlMap = new Dictionary<string, List<object>>();
translatedTextBuilder = new StringBuilder();
}
private bool NeedToTranslate(string text)
{
if (fullTextRegex != null && !fullTextRegex.IsMatch(text))
{
return false;
}
if (eachLineRegex != null)
{
string[] array = text.Split(newLineSymbols, StringSplitOptions.None);
string[] array2 = array;
foreach (string input in array2)
{
if (eachLineRegex.IsMatch(input))
{
return true;
}
}
return false;
}
return true;
}
private IEnumerator ProcessTranslationQueue()
{
isProcessingQueue = true;
while (true)
{
if (uniqueTexts.Count > 200)
{
uniqueTexts = new List<string>();
}
else
{
uniqueTexts.Clear();
}
if (textControlMap.Count > 200)
{
textControlMap = new Dictionary<string, List<object>>();
}
else
{
textControlMap.Clear();
}
int totalCharacterCount = 0;
while (translationQueue.Count > 0 && totalCharacterCount < config.RequestBatchSize)
{
TranslationQueueElement translationRequest = translationQueue.Peek();
string text = translationRequest.Text;
object control = translationRequest.Control;
if (IsNullOrWhiteSpace(text))
{
translationQueue.Dequeue();
continue;
}
if (totalCharacterCount + text.Length > config.RequestBatchSize)
{
break;
}
if (!textControlMap.ContainsKey(text))
{
uniqueTexts.Add(text);
totalCharacterCount += text.Length;
textControlMap[text] = new List<object>();
}
textControlMap[text].Add(control);
translationQueue.Dequeue();
}
if (uniqueTexts.Count > 0)
{
yield return ((MonoBehaviour)this).StartCoroutine(TranslateBatchCoroutine());
}
yield return null;
}
}
private IEnumerator TranslateBatchCoroutine()
{
if (subTexts.Count > 200)
{
subTexts = new List<string>();
}
else
{
subTexts.Clear();
}
if (splitMap.Count > 200)
{
splitMap = new Dictionary<string, List<string>>();
}
else
{
splitMap.Clear();
}
if (translationDictionary.Count > 200)
{
translationDictionary = new Dictionary<string, string>();
}
else
{
translationDictionary.Clear();
}
if (uniqueSubTexts.Count > 200)
{
uniqueSubTexts = new HashSet<string>();
}
else
{
uniqueSubTexts.Clear();
}
if (untranslatedParts.Count > 200)
{
untranslatedParts = new List<string>();
}
string splitPattern = config.RegexForIgnoredSubstringWithinText;
foreach (string text in uniqueTexts)
{
string[] parts2 = (string.IsNullOrEmpty(splitPattern) ? new string[1] { text } : Regex.Split(text, splitPattern));
parts2 = parts2.Where((string part) => !IsNullOrWhiteSpace(part)).ToArray();
bool allTranslated = true;
untranslatedParts.Clear();
string[] array = parts2;
foreach (string part2 in array)
{
if (translationCache.TryGetValue(part2, out var cachedTranslation))
{
translationDictionary[part2] = cachedTranslation;
}
else
{
allTranslated = false;
if (uniqueSubTexts.Add(part2))
{
untranslatedParts.Add(part2);
}
}
cachedTranslation = null;
}
if (allTranslated)
{
StringBuilder translatedTextBuilder = new StringBuilder(text);
string[] array2 = parts2;
foreach (string part3 in array2)
{
if (translationDictionary.TryGetValue(part3, out var translation))
{
translatedTextBuilder.Replace(part3, translation);
}
translation = null;
}
string translatedText = translatedTextBuilder.ToString();
if (textControlMap.TryGetValue(text, out var controls))
{
foreach (object control in controls)
{
OnTranslationFinish(control, text, translatedText);
}
}
controls = null;
}
else
{
splitMap[text] = parts2.ToList();
subTexts.AddRange(untranslatedParts);
}
}
if (subTexts.Count == 0)
{
yield break;
}
if (config.LogRequestedTexts)
{
Debug.Log((object)"请求的文本 RequestedTexts: {");
foreach (string a in subTexts)
{
Debug.Log((object)(" " + a));
}
Debug.Log((object)"}");
}
yield return ((MonoBehaviour)this).StartCoroutine(translationService.StartTranslation(subTexts.ToArray(), delegate(string[] translatedTexts)
{
if (translatedTexts != null)
{
if (translatedTexts.Length == subTexts.Count)
{
for (int k = 0; k < subTexts.Count; k++)
{
translationDictionary[subTexts[k]] = translatedTexts[k].Replace("\\n", "\n");
translationCache.Set(subTexts[k], translatedTexts[k]);
}
{
foreach (string uniqueText in uniqueTexts)
{
if (splitMap.TryGetValue(uniqueText, out var value))
{
this.translatedTextBuilder.Length = 0;
this.translatedTextBuilder.Append(uniqueText);
foreach (string item in value)
{
if (translationDictionary.TryGetValue(item, out var value2))
{
this.translatedTextBuilder.Replace(item, value2);
}
}
string result = this.translatedTextBuilder.ToString();
if (textControlMap.TryGetValue(uniqueText, out var value3))
{
foreach (object item2 in value3)
{
OnTranslationFinish(item2, uniqueText, result);
}
}
}
}
return;
}
}
Debug.LogError((object)"翻译结果数量与请求数量不匹配!");
}
}));
}
public void AddTranslationRequest(string text, object control)
{
if (!NeedToTranslate(text))
{
return;
}
if (translationCache.TryGetValue(text, out var value))
{
OnTranslationFinish(control, text, value);
return;
}
translationQueue.Enqueue(new TranslationQueueElement(text, control));
if (!isProcessingQueue)
{
((MonoBehaviour)this).StartCoroutine(ProcessTranslationQueue());
}
}
private void OnTranslationFinish(object dfControl, string original, string result)
{
if (dfControl == null || result == null)
{
return;
}
dfLabel val = (dfLabel)((dfControl is dfLabel) ? dfControl : null);
if (val != null)
{
if ((Object)(object)val == (Object)null || val.text == null || !(val.text == original) || !((Behaviour)val).isActiveAndEnabled)
{
return;
}
dfFontBase dfFontBase = AutoTranslateModule.instance.fontManager.dfFontBase;
if ((Object)(object)dfFontBase != (Object)null && (Object)(object)val.Font != (Object)(object)dfFontBase)
{
val.Font = dfFontBase;
dfFont val2 = (dfFont)(object)((dfFontBase is dfFont) ? dfFontBase : null);
if (val2 != null)
{
val.Atlas = val2.Atlas;
}
}
val.text = result;
val.OnTextChanged();
return;
}
dfButton val3 = (dfButton)((dfControl is dfButton) ? dfControl : null);
if (val3 != null)
{
if ((Object)(object)val3 == (Object)null || val3.text == null || !(val3.text == original) || !((Behaviour)val3).isActiveAndEnabled)
{
return;
}
dfFontBase dfFontBase2 = AutoTranslateModule.instance.fontManager.dfFontBase;
if ((Object)(object)dfFontBase2 != (Object)null && (Object)(object)val3.Font != (Object)(object)dfFontBase2)
{
val3.Font = dfFontBase2;
dfFont val4 = (dfFont)(object)((dfFontBase2 is dfFont) ? dfFontBase2 : null);
if (val4 != null)
{
((dfInteractiveBase)val3).Atlas = val4.Atlas;
}
}
val3.text = result;
((dfControl)val3).Invalidate();
return;
}
SLabel val5 = (SLabel)((dfControl is SLabel) ? dfControl : null);
if (val5 != null)
{
if (val5 != null && val5.Text != null && val5.Text == original)
{
val5.Text = result;
}
return;
}
tk2dTextMesh val6 = (tk2dTextMesh)((dfControl is tk2dTextMesh) ? dfControl : null);
if (val6 != null && !((Object)(object)val6 == (Object)null) && val6.data != null && val6.data.text != null && val6.data.text == original)
{
tk2dFontData tk2dFont = AutoTranslateModule.instance.fontManager.tk2dFont;
if ((Object)(object)tk2dFont != (Object)null && (Object)(object)val6.font != (Object)(object)tk2dFont)
{
FontManager.SetTextMeshFont(val6, tk2dFont);
}
val6.data.text = result;
val6.SetNeedUpdate((UpdateFlags)1);
}
}
private bool IsNullOrWhiteSpace(string value)
{
return string.IsNullOrEmpty(value) || value.Trim().Length == 0;
}
public void ReadAndRestoreTranslationCache(string filePath)
{
try
{
if (!File.Exists(filePath))
{
Debug.LogError((object)"加载预设翻译出现错误,文件不存在。Error loading preset translation, file does not exist.");
return;
}
string text = File.ReadAllText(filePath);
Dictionary<string, string> dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(text);
foreach (KeyValuePair<string, string> item in dictionary)
{
translationCache.Set(item.Key, item.Value);
}
Debug.Log((object)"加载预设翻译完毕。Loading preset translation completed.");
}
catch (Exception ex)
{
Debug.LogError((object)("加载预设翻译出现错误。Error loading preset translation: " + ex.Message));
}
}
}
public class TranslationManagerConfig
{
internal AutoTranslateModule.TranslationAPIType TranslationAPI;
internal string RegexForFullTextNeedToTranslate;
internal string RegexForEachLineNeedToTranslate;
internal string RegexForIgnoredSubstringWithinText;
internal int RequestBatchSize;
internal int MaxRetryCount;
internal int TranslationCacheCapacity;
internal string PresetTranslations;
internal bool LogRequestedTexts;
internal string TencentSecretId;
internal string TencentSecretKey;
internal string TencentSourceLanguage;
internal string TencentTargetLanguage;
internal string TencentRegion;
internal string BaiduAppId;
internal string BaiduSecretKey;
internal string BaiduSourceLanguage;
internal string BaiduTargetLanguage;
internal string AzureSubscriptionKey;
internal string AzureSourceLanguage;
internal string AzureTargetLanguage;
internal string AzureRegion;
internal bool CheckConfigValues()
{
if (!Enum.IsDefined(typeof(AutoTranslateModule.TranslationAPIType), TranslationAPI))
{
return false;
}
switch (TranslationAPI)
{
case AutoTranslateModule.TranslationAPIType.Tencent:
if (IsNullOrEmptyString(TencentSecretId))
{
return false;
}
if (IsNullOrEmptyString(TencentSecretKey))
{
return false;
}
break;
case AutoTranslateModule.TranslationAPIType.Baidu:
if (IsNullOrEmptyString(BaiduAppId))
{
return false;
}
if (IsNullOrEmptyString(BaiduSecretKey))
{
return false;
}
break;
case AutoTranslateModule.TranslationAPIType.Azure:
if (IsNullOrEmptyString(AzureSubscriptionKey))
{
return false;
}
break;
}
return true;
}
private static bool IsNullOrEmptyString(string str)
{
return str == null || str == string.Empty;
}
}
internal class TranslationQueueElement
{
public string Text { get; set; }
public object Control { get; set; }
public TranslationQueueElement(string translation, object control)
{
Text = translation;
Control = control;
}
}