Decompiled source of HexResourceTrackerLocalization v0.1.0

HexResourceTrackerLocalization.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using LocalizationShared;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("lnx")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © lnx")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0")]
[assembly: AssemblyProduct("HexResourceTrackerLocalization")]
[assembly: AssemblyTitle("HexResourceTrackerLocalization")]
[assembly: AssemblyMetadata("Author", "lnx")]
[assembly: AssemblyVersion("0.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LocalizationShared
{
	internal sealed class TranslationConfig : IDisposable
	{
		private readonly ConfigFile config;

		private readonly Action<string> warning;

		private readonly Dictionary<string, ConfigEntry<string>> entries = new Dictionary<string, ConfigEntry<string>>(StringComparer.Ordinal);

		private readonly Dictionary<string, string> defaults;

		private readonly Dictionary<string, string> aliases = new Dictionary<string, string>(StringComparer.Ordinal);

		private readonly Dictionary<string, string> lastValues = new Dictionary<string, string>(StringComparer.Ordinal);

		private readonly Dictionary<string, string> resolved = new Dictionary<string, string>(StringComparer.Ordinal);

		private DateTime nextCheck;

		private DateTime observedWrite;

		private DateTime loadedWrite;

		private long observedLength;

		private long loadedLength;

		private int changed;

		internal TranslationConfig(ConfigFile config, TranslationDefinition[] definitions, Action<string> warning, Func<string, string> gameLocalize = null)
		{
			this.config = config;
			defaults = new Dictionary<string, string>(StringComparer.Ordinal);
			this.warning = warning;
			bool saveOnConfigSet = config.SaveOnConfigSet;
			config.SaveOnConfigSet = false;
			try
			{
				HashSet<ConfigDefinition> hashSet = new HashSet<ConfigDefinition>();
				Dictionary<string, string> dictionary = MigrateLegacySection(config, hashSet);
				Dictionary<string, string> dictionary2 = MigrateDuplicates(config, definitions);
				foreach (TranslationDefinition translationDefinition in definitions)
				{
					string text = translationDefinition.Default;
					if (translationDefinition.GameKey != null && gameLocalize != null)
					{
						string text2 = gameLocalize(translationDefinition.GameKey);
						if (!string.IsNullOrEmpty(text2) && text2 != translationDefinition.GameKey && !text2.StartsWith("[", StringComparison.Ordinal))
						{
							text = text2;
						}
					}
					defaults.Add(translationDefinition.Id, text);
					string text3 = "Исходный текст: " + translationDefinition.Source;
					ConfigEntry<string> val = config.Bind<string>(translationDefinition.Section, translationDefinition.Key, text, text3);
					if (!hashSet.Contains(((ConfigEntryBase)val).Definition) && dictionary.TryGetValue(KeyFor(translationDefinition.Source), out var value))
					{
						string text4 = TomlTypeConverter.ConvertToValue<string>(value);
						if (text4 != translationDefinition.LegacyDefault)
						{
							val.Value = text4;
						}
					}
					entries.Add(translationDefinition.Id, val);
					if (translationDefinition.AliasSource == null)
					{
						continue;
					}
					aliases.Add(translationDefinition.AliasSource, translationDefinition.Id);
					if (!dictionary2.TryGetValue(translationDefinition.Id, out var value2))
					{
						dictionary.TryGetValue(KeyFor(translationDefinition.AliasSource), out value2);
					}
					if (value2 != null && val.Value == text)
					{
						string text5 = TomlTypeConverter.ConvertToValue<string>(value2);
						if (text5 != translationDefinition.LegacyDefault)
						{
							val.Value = text5;
						}
					}
				}
				config.Save();
			}
			finally
			{
				config.SaveOnConfigSet = saveOnConfigSet;
			}
			FileInfo fileInfo = new FileInfo(config.ConfigFilePath);
			loadedWrite = (observedWrite = fileInfo.LastWriteTimeUtc);
			loadedLength = (observedLength = fileInfo.Length);
			config.SettingChanged += OnSettingChanged;
			config.ConfigReloaded += OnReloaded;
		}

		private static Dictionary<string, string> MigrateDuplicates(ConfigFile config, TranslationDefinition[] definitions)
		{
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Expected O, but got Unknown
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			Dictionary<ConfigDefinition, TranslationDefinition> dictionary2 = definitions.Where((TranslationDefinition d) => d.AliasKey != null).ToDictionary((Func<TranslationDefinition, ConfigDefinition>)((TranslationDefinition d) => new ConfigDefinition(d.Section, d.AliasKey)));
			if (dictionary2.Count == 0 || !File.Exists(config.ConfigFilePath))
			{
				return dictionary;
			}
			List<string> list = new List<string>();
			string text = "";
			string[] array = File.ReadAllLines(config.ConfigFilePath);
			foreach (string text2 in array)
			{
				string text3 = text2.Trim();
				if (text3.StartsWith("[", StringComparison.Ordinal) && text3.EndsWith("]", StringComparison.Ordinal))
				{
					text = text3.Substring(1, text3.Length - 2);
				}
				int num2 = text3.IndexOf('=');
				if (text.Length != 0 && num2 > 0 && !text3.StartsWith("#", StringComparison.Ordinal) && !text3.StartsWith("[", StringComparison.Ordinal) && dictionary2.TryGetValue(new ConfigDefinition(text, text3.Substring(0, num2).Trim()), out var value))
				{
					dictionary[value.Id] = text3.Substring(num2 + 1).Trim();
				}
				else
				{
					list.Add(text2);
				}
			}
			if (dictionary.Count != 0)
			{
				string text4 = config.ConfigFilePath + ".v1.2.bak";
				int num3 = 1;
				while (File.Exists(text4))
				{
					text4 = config.ConfigFilePath + ".v1.2.bak." + num3;
					num3++;
				}
				File.Copy(config.ConfigFilePath, text4);
				File.WriteAllLines(config.ConfigFilePath, list, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				config.Reload();
			}
			return dictionary;
		}

		private static Dictionary<string, string> MigrateLegacySection(ConfigFile config, HashSet<ConfigDefinition> existing)
		{
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Expected O, but got Unknown
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			if (!File.Exists(config.ConfigFilePath))
			{
				return dictionary;
			}
			List<string> list = new List<string>();
			string text = "";
			bool flag = false;
			string[] array = File.ReadAllLines(config.ConfigFilePath);
			foreach (string text2 in array)
			{
				string text3 = text2.Trim();
				if (text3.StartsWith("[", StringComparison.Ordinal) && text3.EndsWith("]", StringComparison.Ordinal))
				{
					text = text3.Substring(1, text3.Length - 2);
				}
				bool flag2 = text == "Translations";
				flag = flag || flag2;
				if (!flag2)
				{
					list.Add(text2);
				}
				int num = text3.IndexOf('=');
				if (num > 0 && !text3.StartsWith("#", StringComparison.Ordinal) && !text3.StartsWith("[", StringComparison.Ordinal))
				{
					string text4 = text3.Substring(0, num).Trim();
					if (flag2)
					{
						dictionary[text4] = text3.Substring(num + 1).Trim();
					}
					else if (text.Length != 0)
					{
						existing.Add(new ConfigDefinition(text, text4));
					}
				}
			}
			if (flag)
			{
				string text5 = config.ConfigFilePath + ".v1.1.bak";
				int num2 = 1;
				while (File.Exists(text5))
				{
					text5 = config.ConfigFilePath + ".v1.1.bak." + num2;
					num2++;
				}
				File.Copy(config.ConfigFilePath, text5);
				File.WriteAllLines(config.ConfigFilePath, list, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				config.Reload();
			}
			return dictionary;
		}

		internal static string KeyFor(string source)
		{
			string text = Regex.Replace(source, "[^\\p{L}\\p{Nd}]+", "_").Trim('_');
			if (text.Length > 48)
			{
				text = text.Substring(0, 48);
			}
			using SHA256 sHA = SHA256.Create();
			string text2 = BitConverter.ToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(source))).Replace("-", "").Substring(0, 12);
			return ((text.Length == 0) ? "Text" : text) + "_" + text2;
		}

		internal string Get(string source)
		{
			if (aliases.TryGetValue(source, out var value))
			{
				source = value;
			}
			if (!entries.TryGetValue(source, out var value2))
			{
				return source;
			}
			string text = value2.Value ?? "";
			if (lastValues.TryGetValue(source, out var value3) && value3 == text)
			{
				return resolved[source];
			}
			lastValues[source] = text;
			if (!ValidFormat(source, text))
			{
				warning("Invalid translation template [" + ((ConfigEntryBase)value2).Definition.Key + "]; using the built-in text. Preserve its placeholders.");
				text = defaults[source];
			}
			resolved[source] = text;
			return text;
		}

		private static bool ValidFormat(string source, string value)
		{
			if (!source.Contains("{0}"))
			{
				return true;
			}
			object[] array = ((!source.Contains("{1}")) ? new object[1] { "__LOC_ARG_0__" } : new object[2] { "__LOC_ARG_0__", "__LOC_ARG_1__" });
			try
			{
				string formatted = string.Format(CultureInfo.InvariantCulture, value, array);
				return array.All((object argument) => formatted.Contains((string)argument));
			}
			catch (FormatException)
			{
				return false;
			}
		}

		internal bool Poll()
		{
			if (DateTime.UtcNow >= nextCheck)
			{
				nextCheck = DateTime.UtcNow.AddSeconds(1.0);
				try
				{
					FileInfo fileInfo = new FileInfo(config.ConfigFilePath);
					if (fileInfo.Exists)
					{
						if (fileInfo.LastWriteTimeUtc != observedWrite || fileInfo.Length != observedLength)
						{
							observedWrite = fileInfo.LastWriteTimeUtc;
							observedLength = fileInfo.Length;
						}
						else if (observedWrite != loadedWrite || observedLength != loadedLength)
						{
							bool saveOnConfigSet = config.SaveOnConfigSet;
							config.SaveOnConfigSet = false;
							try
							{
								config.Reload();
							}
							finally
							{
								config.SaveOnConfigSet = saveOnConfigSet;
							}
							loadedWrite = observedWrite;
							loadedLength = observedLength;
						}
					}
				}
				catch (Exception ex) when (ex is IOException || ex is UnauthorizedAccessException)
				{
					warning("Cannot reload translation config: " + ex.Message);
				}
			}
			return Interlocked.Exchange(ref changed, 0) != 0;
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs args)
		{
			Interlocked.Exchange(ref changed, 1);
		}

		private void OnReloaded(object sender, EventArgs args)
		{
			Interlocked.Exchange(ref changed, 1);
		}

		public void Dispose()
		{
			config.SettingChanged -= OnSettingChanged;
			config.ConfigReloaded -= OnReloaded;
		}
	}
	internal sealed class TranslationDefinition
	{
		internal readonly string Id;

		internal readonly string Source;

		internal readonly string Section;

		internal readonly string Key;

		internal readonly string Default;

		internal readonly string LegacyDefault;

		internal readonly string GameKey;

		internal readonly string AliasSource;

		internal readonly string AliasKey;

		internal TranslationDefinition(string id, string source, string section, string key, string defaultValue, string legacyDefault, string gameKey = null, string aliasSource = null, string aliasKey = null)
		{
			Id = id;
			Source = source;
			Section = section;
			Key = key;
			Default = defaultValue;
			LegacyDefault = legacyDefault;
			GameKey = gameKey;
			AliasSource = aliasSource;
			AliasKey = aliasKey;
		}
	}
}
namespace HexResourceTrackerLocalization
{
	[BepInPlugin("lordn.hexresourcetracker.localization", "HexResourceTracker Localization", "0.1.0")]
	[BepInDependency("com.hex.resourcetracker", "1.4.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "lordn.hexresourcetracker.localization";

		public const string OriginalGuid = "com.hex.resourcetracker";

		private Harmony harmony;

		private bool initialized;

		private static FieldInfo panelField;

		private static FieldInfo dungeonPinsField;

		private static PropertyInfo modelPinProperty;

		private static GameObject panel;

		private static readonly Dictionary<TMP_Text, string> Labels = new Dictionary<TMP_Text, string>();

		private void Awake()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			harmony = new Harmony("lordn.hexresourcetracker.localization");
			try
			{
				Translations.Initialize(((BaseUnityPlugin)this).Config, delegate(string message)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)message);
				});
				PluginInfo val = Chainloader.PluginInfos["com.hex.resourcetracker"];
				Assembly assembly = ((object)val.Instance).GetType().Assembly;
				Type type = assembly.GetType("HexResourceTracker.Core.ResourceTrackerMapOverlay", throwOnError: true);
				Type type2 = assembly.GetType("HexResourceTracker.DungeonPinManager", throwOnError: true);
				Type? type3 = assembly.GetType("HexResourceTracker.DungeonPinModel", throwOnError: true);
				panelField = AccessTools.Field(type, "_panel");
				dungeonPinsField = AccessTools.Field(type2, "DungeonPins");
				modelPinProperty = AccessTools.Property(type3, "Pin");
				if (panelField == null || dungeonPinsField == null || modelPinProperty == null)
				{
					throw new MissingMemberException("HexResourceTracker layout is incompatible.");
				}
				Patch(AccessTools.Method(type, "Create", Type.EmptyTypes, (Type[])null), "OverlayCreated");
				Patch(AccessTools.Method(typeof(PinNameData), "SetTextAndGameObject", new Type[1] { typeof(GameObject) }, (Type[])null), "PinLabelCreated");
				Patch(AccessTools.Method(typeof(Localization), "SetLanguage", new Type[1] { typeof(string) }, (Type[])null), "LanguageChanged");
				Patch(AccessTools.Method(type2, "TryAddDungeonPin", (Type[])null, (Type[])null), "DungeonPinAdded");
				OverlayCreated();
				initialized = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Russian localization loaded for HexResourceTracker " + val.Metadata.Version));
			}
			catch (Exception ex)
			{
				Translations.Config?.Dispose();
				Translations.Config = null;
				harmony.UnpatchSelf();
				RestoreLabels();
				((BaseUnityPlugin)this).Logger.LogError((object)("Localization disabled: " + ex));
			}
		}

		private void Update()
		{
			if (initialized && Translations.Config != null && Translations.Config.Poll())
			{
				LanguageChanged();
			}
		}

		private void Patch(MethodBase target, string postfix)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			if (target == null)
			{
				throw new MissingMethodException(postfix);
			}
			harmony.Patch(target, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), postfix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static void OverlayCreated()
		{
			object? value = panelField.GetValue(null);
			GameObject val = (GameObject)((value is GameObject) ? value : null);
			if ((Object)(object)val == (Object)null)
			{
				panel = null;
				Labels.Clear();
				return;
			}
			if ((Object)(object)panel != (Object)(object)val)
			{
				panel = val;
				Labels.Clear();
				TMP_Text[] componentsInChildren = val.GetComponentsInChildren<TMP_Text>(true);
				foreach (TMP_Text val2 in componentsInChildren)
				{
					if (Translations.Text.ContainsKey(val2.text))
					{
						Labels[val2] = val2.text;
					}
				}
			}
			foreach (KeyValuePair<TMP_Text, string> label in Labels)
			{
				if ((Object)(object)label.Key != (Object)null)
				{
					label.Key.text = Translations.Translate(label.Value);
					label.Key.enableAutoSizing = Translations.Russian;
					label.Key.fontSizeMin = 9f;
					label.Key.fontSizeMax = ((((Object)((Component)label.Key).gameObject).name == "Label") ? 14f : 12f);
					if (!Translations.Russian)
					{
						label.Key.fontSize = label.Key.fontSizeMax;
					}
				}
			}
		}

		private static IEnumerable<PinData> TrackedPins()
		{
			if (!(dungeonPinsField.GetValue(null) is IEnumerable enumerable))
			{
				yield break;
			}
			foreach (object item in enumerable)
			{
				object? value = modelPinProperty.GetValue(item, null);
				PinData val = (PinData)((value is PinData) ? value : null);
				if (val != null)
				{
					yield return val;
				}
			}
		}

		private static void PinLabelCreated(PinNameData __instance)
		{
			foreach (PinData item in TrackedPins())
			{
				if (item == __instance.ParentPin)
				{
					TranslatePin(item);
					break;
				}
			}
		}

		private static void TranslatePin(PinData pin)
		{
			PinNameData namePinData = pin.m_NamePinData;
			TMP_Text val = ((namePinData != null) ? namePinData.PinNameText : null);
			if ((Object)(object)val != (Object)null)
			{
				val.text = Translations.TranslatePin(pin.m_name);
			}
		}

		private static void RefreshPins()
		{
			foreach (PinData item in TrackedPins())
			{
				TranslatePin(item);
			}
		}

		private static void DungeonPinAdded(bool __result)
		{
			if (__result && dungeonPinsField.GetValue(null) is IList { Count: not 0 } list)
			{
				object? value = modelPinProperty.GetValue(list[list.Count - 1], null);
				PinData val = (PinData)((value is PinData) ? value : null);
				if (val != null)
				{
					TranslatePin(val);
				}
			}
		}

		private static void LanguageChanged()
		{
			OverlayCreated();
			RefreshPins();
		}

		private static void RestoreLabels()
		{
			foreach (KeyValuePair<TMP_Text, string> label in Labels)
			{
				if ((Object)(object)label.Key != (Object)null)
				{
					label.Key.text = label.Value;
					label.Key.enableAutoSizing = false;
					label.Key.fontSize = ((((Object)((Component)label.Key).gameObject).name == "Label") ? 14f : 12f);
				}
			}
			Labels.Clear();
			panel = null;
			if (dungeonPinsField == null || modelPinProperty == null)
			{
				return;
			}
			foreach (PinData item in TrackedPins())
			{
				PinNameData namePinData = item.m_NamePinData;
				if ((Object)(object)((namePinData != null) ? namePinData.PinNameText : null) != (Object)null)
				{
					item.m_NamePinData.PinNameText.text = item.m_name;
				}
			}
		}

		private void OnDestroy()
		{
			Translations.Config?.Dispose();
			Translations.Config = null;
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			RestoreLabels();
		}
	}
	internal static class Translations
	{
		internal static TranslationConfig Config;

		internal static readonly TranslationDefinition[] Definitions = new TranslationDefinition[40]
		{
			new TranslationDefinition("Map Tracking", "Map Tracking", "1 - Interface", "Map Tracking", "Отслеживание", "Отслеживание"),
			new TranslationDefinition("Resources", "Resources", "1 - Interface", "Resources", "Ресурсы", "Ресурсы"),
			new TranslationDefinition("Deposits", "Deposits", "1 - Interface", "Deposits", "Месторождения", "Месторождения"),
			new TranslationDefinition("Dungeons", "Dungeons", "1 - Interface", "Dungeons", "Подземелья", "Подземелья"),
			new TranslationDefinition("Mushrooms", "Mushrooms", "2 - Resource labels", "Mushrooms", "Гриб", "Грибы", "$item_mushroomcommon"),
			new TranslationDefinition("Dandelions", "Dandelions", "2 - Resource labels", "Dandelions", "Одуванчик", "Одуванчики", "$item_dandelion"),
			new TranslationDefinition("Raspberries", "Raspberries", "2 - Resource labels", "Raspberries", "Малина", "Малина", "$item_raspberries"),
			new TranslationDefinition("Blueberries", "Blueberries", "2 - Resource labels", "Blueberries", "Черника", "Черника", "$item_blueberries"),
			new TranslationDefinition("Thistle", "Thistle", "2 - Resource labels", "Thistle", "Чертополох", "Чертополох", "$item_thistle"),
			new TranslationDefinition("Carrot Seeds", "Carrot Seeds", "2 - Resource labels", "Carrot Seeds", "Семена моркови", "Семена моркови", "$item_carrotseeds"),
			new TranslationDefinition("Turnip Seeds", "Turnip Seeds", "2 - Resource labels", "Turnip Seeds", "Семена репы", "Семена репы", "$item_turnipseeds"),
			new TranslationDefinition("Dragon Eggs", "Dragon Eggs", "2 - Resource labels", "Dragon Eggs", "Яйцо дракона", "Драконьи яйца", "$item_dragonegg"),
			new TranslationDefinition("Onion Seeds", "Onion Seeds", "2 - Resource labels", "Onion Seeds", "Семена лука", "Семена лука", "$item_onionseeds"),
			new TranslationDefinition("Flax", "Flax", "2 - Resource labels", "Flax", "Лен", "Лён", "$item_flax"),
			new TranslationDefinition("Barley", "Barley", "2 - Resource labels", "Barley", "Ячмень", "Ячмень", "$item_barley"),
			new TranslationDefinition("Cloudberries", "Cloudberries", "2 - Resource labels", "Cloudberries", "Морошка", "Морошка", "$item_cloudberries"),
			new TranslationDefinition("Jotun Puffs", "Jotun Puffs", "2 - Resource labels", "Jotun Puffs", "Гриб Йотунов", "Дымовики йотунов", "$item_jotunpuffs"),
			new TranslationDefinition("Magecap", "Magecap", "2 - Resource labels", "Magecap", "Волшебный гриб", "Колпак мага", "$item_magecap"),
			new TranslationDefinition("Vineberries", "Vineberries", "2 - Resource labels", "Vineberries", "Гроздь лозовых ягод", "Виноград", "$item_vineberry"),
			new TranslationDefinition("Smoke Puffs", "Smoke Puffs", "2 - Resource labels", "Smoke Puffs", "Дымчатый гриб", "Дымовики", "$item_smokepuff"),
			new TranslationDefinition("Fiddleheads", "Fiddleheads", "2 - Resource labels", "Fiddleheads", "Рахис", "Побеги папоротника", "$item_fiddleheadfern"),
			new TranslationDefinition("Lingonberries", "Lingonberries", "2 - Resource labels", "Lingonberries", "Брусника", "Брусника", "$item_lingonberries"),
			new TranslationDefinition("Kale Seeds", "Kale Seeds", "2 - Resource labels", "Kale Seeds", "Семена кале", "Семена капусты", "$item_kaleseeds"),
			new TranslationDefinition("Copper", "Copper", "2 - Resource labels", "Copper", "Залежи меди", "Медь", "$piece_deposit_copper"),
			new TranslationDefinition("Silver", "Silver", "2 - Resource labels", "Silver", "Серебряная жила", "Серебро", "$piece_deposit_silvervein"),
			new TranslationDefinition("Giant Skull", "Giant Skull", "2 - Resource labels", "Giant Skull", "Окаменевшая кость", "Череп великана", "$piece_giant_bone"),
			new TranslationDefinition("Flametal", "Flametal", "2 - Resource labels", "Flametal", "Огнеметаллическая руда", "Огнеметалл", "$item_flametalore"),
			new TranslationDefinition("Burial Chambers", "Burial Chambers", "3 - Dungeon labels", "Burial Chambers", "Погребальные комнаты", "Погребальные камеры", "$location_forestcrypt"),
			new TranslationDefinition("Sunken Crypts", "Sunken Crypts", "3 - Dungeon labels", "Sunken Crypts", "Затонувшие склепы", "Затонувшие склепы", "$location_sunkencrypt"),
			new TranslationDefinition("Frost Caves", "Frost Caves", "3 - Dungeon labels", "Frost Caves", "Ледяные пещеры", "Морозные пещеры", "$location_mountaincave"),
			new TranslationDefinition("Infested Mines", "Infested Mines", "3 - Dungeon labels", "Infested Mines", "Зараженный рудник", "Заражённые шахты", "$location_dvergrtown"),
			new TranslationDefinition("Morkhalla", "Morkhalla", "3 - Dungeon labels", "Morkhalla", "Мёркхалла", "Моркхалла", "$location_morkhalla"),
			new TranslationDefinition("Winding Tunnels", "Winding Tunnels", "3 - Dungeon labels", "Winding Tunnels", "Извилистые тоннели", "Извилистые туннели", "$location_thehole"),
			new TranslationDefinition("Pin: Burial Chamber", "Burial Chamber", "4 - Map pins", "Burial Chambers", "Погребальные комнаты", "Погребальная камера", "$location_forestcrypt"),
			new TranslationDefinition("Pin: Sunken Crypt", "Sunken Crypt", "4 - Map pins", "Sunken Crypts", "Затонувшие склепы", "Затонувший склеп", "$location_sunkencrypt"),
			new TranslationDefinition("Pin: Frost Cave", "Frost Cave", "4 - Map pins", "Frost Caves", "Ледяные пещеры", "Морозная пещера", "$location_mountaincave"),
			new TranslationDefinition("Pin: Infested Mine", "Infested Mine", "4 - Map pins", "Infested Mines", "Зараженный рудник", "Заражённая шахта", "$location_dvergrtown"),
			new TranslationDefinition("Pin: Morkhalla", "Morkhalla", "4 - Map pins", "Morkhalla", "Мёркхалла", "Моркхалла", "$location_morkhalla"),
			new TranslationDefinition("Pin: Winding Tunnel", "Winding Tunnel", "4 - Map pins", "Winding Tunnels", "Извилистые тоннели", "Извилистый туннель", "$location_thehole"),
			new TranslationDefinition("Pin: Dungeon", "Dungeon", "4 - Map pins", "Unknown dungeon", "Подземелье", "Подземелье")
		};

		internal static readonly Dictionary<string, string> Text = Definitions.ToDictionary<TranslationDefinition, string, string>((TranslationDefinition entry) => entry.Id, (TranslationDefinition entry) => entry.Default, StringComparer.Ordinal);

		internal static bool Russian
		{
			get
			{
				if (Localization.instance != null)
				{
					return string.Equals(Localization.instance.GetSelectedLanguage(), "Russian", StringComparison.OrdinalIgnoreCase);
				}
				return false;
			}
		}

		internal static void Initialize(ConfigFile config, Action<string> warning)
		{
			Config = new TranslationConfig(config, Definitions, warning, GameDefault);
		}

		private static string GameDefault(string token)
		{
			object? obj = typeof(Localization).GetField("m_instance", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null);
			Localization val = (Localization)((obj is Localization) ? obj : null);
			if (val == null || !(val.GetSelectedLanguage() == "Russian"))
			{
				return null;
			}
			return val.Localize(token);
		}

		internal static string Translate(string value)
		{
			return Resolve(value, value);
		}

		internal static string TranslatePin(string value)
		{
			return Resolve("Pin: " + value, value);
		}

		private static string Resolve(string id, string original)
		{
			if (original == null || !Russian || !Text.TryGetValue(id, out var value))
			{
				return original;
			}
			if (Config != null)
			{
				return Config.Get(id);
			}
			return value;
		}
	}
}