Decompiled source of RomesteadLocalizationAPI v1.0.1

BepInEx/plugins/RomesteadLocalizationAPI.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text.Json;
using BepInEx;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.NET.Common;
using Candide;
using Candide.GameModels.Helpers;
using HarmonyLib;
using RomesteadLocalizationAPI.Api;
using Shared.Text;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Romestead Localization API")]
[assembly: AssemblyDescription("Romestead Localization API by Ice Box Studio")]
[assembly: AssemblyCompany("Ice Box Studio")]
[assembly: AssemblyProduct("Romestead Localization API")]
[assembly: AssemblyCopyright("Copyright (C) 2026 Ice Box Studio All rights reserved.")]
[assembly: ComVisible(false)]
[assembly: Guid("6f52443b-3b87-402a-9965-44b01b12c078")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]
[assembly: AssemblyVersion("1.0.1.0")]
[module: RefSafetyRules(11)]
namespace RomesteadLocalizationAPI
{
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "IceBoxStudio.Romestead.LocalizationAPI";

		public const string PLUGIN_NAME = "Romestead Localization API";

		public const string PLUGIN_VERSION = "1.0.1";

		public const string PLUGIN_AUTHOR = "Ice Box Studio";

		public const string PLUGIN_AUTHOR_URL = "https://steamcommunity.com/id/ibox666/";
	}
	[BepInPlugin("IceBoxStudio.Romestead.LocalizationAPI", "Romestead Localization API", "1.0.1")]
	public sealed class RomesteadLocalizationAPI : BasePlugin
	{
		private Harmony _harmony;

		public static RomesteadLocalizationAPI Instance { get; private set; }

		internal static ManualLogSource Logger { get; private set; }

		public override void Load()
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			Instance = this;
			Logger = ((BasePlugin)this).Log;
			bool flag = default(bool);
			try
			{
				_harmony = new Harmony("IceBoxStudio.Romestead.LocalizationAPI");
				_harmony.PatchAll(Assembly.GetExecutingAssembly());
				RomesteadLocalization.Apply();
				ManualLogSource logger = Logger;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(13, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("Romestead Localization API");
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" initialized.");
				}
				logger.LogInfo(val);
			}
			catch (Exception ex)
			{
				ManualLogSource logger2 = Logger;
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(24, 3, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>("Romestead Localization API");
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" initialization error: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("\n");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.StackTrace);
				}
				logger2.LogError(val2);
			}
		}
	}
}
namespace RomesteadLocalizationAPI.Patches
{
	[HarmonyPatch(typeof(LocalizationManager), "SetLocalization")]
	internal static class LocalizationManagerPatch
	{
		private static void Postfix(string locale)
		{
			RomesteadLocalization.OnGameLocalizationLoaded(locale);
		}
	}
}
namespace RomesteadLocalizationAPI.Api
{
	public static class RomesteadLocales
	{
		public const string English = "en";

		public const string ChineseSimplified = "zh_CN";

		public const string ChineseTraditional = "zh_TW";

		public const string Czech = "cs_CZ";

		public const string German = "de_DE";

		public const string Greek = "el_GR";

		public const string Spanish = "es_ES";

		public const string French = "fr_FR";

		public const string Italian = "it_IT";

		public const string Japanese = "ja_JP";

		public const string Korean = "ko_KR";

		public const string Dutch = "nl_NL";

		public const string Polish = "pl_PL";

		public const string PortugueseBrazil = "pt_BR";

		public const string PortuguesePortugal = "pt_PT";

		public const string Russian = "ru_RU";

		public const string Slovak = "sk_SK";

		public const string Swedish = "sv_SE";

		public const string Thai = "th_TH";

		public const string Turkish = "tr_TR";

		public const string Ukrainian = "uk_UA";

		internal static readonly string[] Known = new string[21]
		{
			"en", "zh_CN", "zh_TW", "cs_CZ", "de_DE", "el_GR", "es_ES", "fr_FR", "it_IT", "ja_JP",
			"ko_KR", "nl_NL", "pl_PL", "pt_BR", "pt_PT", "ru_RU", "sk_SK", "sv_SE", "th_TH", "tr_TR",
			"uk_UA"
		};
	}
	public static class RomesteadLocalization
	{
		private sealed class Entry
		{
			public string SourceId;

			public readonly Dictionary<string, string> Texts = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

			public Entry(string sourceId)
			{
				SourceId = sourceId;
			}
		}

		private const string TestingLocale = "<testing>";

		private static readonly object Sync = new object();

		private static readonly Dictionary<string, Entry> Entries = new Dictionary<string, Entry>(StringComparer.Ordinal);

		private static string _currentLocale = "en";

		public static string CurrentLocale => DetectCurrentLocale();

		public static event Action<string> LanguageChanged;

		public static event Action<string> LocalizationApplied;

		public static RomesteadLocalizer For(string sourceId)
		{
			ValidateSourceId(sourceId);
			return new RomesteadLocalizer(sourceId);
		}

		public static void RegisterJsonDirectory(string sourceId, string directory)
		{
			ValidateSourceId(sourceId);
			if (string.IsNullOrWhiteSpace(directory))
			{
				throw new ArgumentException("Localization directory cannot be empty.", "directory");
			}
			if (!Directory.Exists(directory))
			{
				throw new DirectoryNotFoundException(directory);
			}
			Dictionary<string, Dictionary<string, string>> dictionary = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
			string[] files = Directory.GetFiles(directory, "*.json", SearchOption.TopDirectoryOnly);
			Array.Sort(files, (IComparer<string>?)StringComparer.OrdinalIgnoreCase);
			string[] array = files;
			foreach (string path in array)
			{
				string localeFromJsonFile = GetLocaleFromJsonFile(path);
				foreach (KeyValuePair<string, string> item in ReadJsonTextMap(path))
				{
					AddBundleText(dictionary, localeFromJsonFile, item.Key, item.Value);
				}
			}
			RegisterBundle(sourceId, dictionary);
		}

		public static void RegisterJson(string sourceId, string path)
		{
			ValidateSourceId(sourceId);
			RegisterBundle(sourceId, ReadJsonBundle(path));
		}

		public static void RegisterText(string sourceId, string locale, string key, string text)
		{
			ValidateSourceId(sourceId);
			ValidateLocale(locale);
			ValidateKey(key);
			if (text == null)
			{
				throw new ArgumentNullException("text");
			}
			Dictionary<string, Dictionary<string, string>> dictionary = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
			AddBundleText(dictionary, locale, key, text);
			RegisterBundle(sourceId, dictionary);
		}

		public static void RegisterTexts(string sourceId, string locale, IReadOnlyDictionary<string, string> texts)
		{
			ValidateSourceId(sourceId);
			ValidateLocale(locale);
			if (texts == null)
			{
				throw new ArgumentNullException("texts");
			}
			Dictionary<string, Dictionary<string, string>> dictionary = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
			foreach (KeyValuePair<string, string> text in texts)
			{
				ValidateKey(text.Key);
				if (text.Value == null)
				{
					throw new ArgumentException("Localization text cannot be null: " + text.Key, "texts");
				}
				AddBundleText(dictionary, locale, text.Key, text.Value);
			}
			RegisterBundle(sourceId, dictionary);
		}

		public static void RegisterTexts(string sourceId, params (string Locale, string Key, string Text)[] texts)
		{
			ValidateSourceId(sourceId);
			if (texts == null)
			{
				throw new ArgumentNullException("texts");
			}
			Dictionary<string, Dictionary<string, string>> dictionary = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
			for (int i = 0; i < texts.Length; i++)
			{
				(string, string, string) tuple = texts[i];
				ValidateLocale(tuple.Item1);
				ValidateKey(tuple.Item2);
				if (tuple.Item3 == null)
				{
					throw new ArgumentException("Localization text cannot be null: " + tuple.Item1 + "." + tuple.Item2, "texts");
				}
				AddBundleText(dictionary, tuple.Item1, tuple.Item2, tuple.Item3);
			}
			RegisterBundle(sourceId, dictionary);
		}

		public static string Text(string key, params object[] args)
		{
			return GetText(key, args);
		}

		public static string TextFor(string sourceId, string key, params object[] args)
		{
			return GetTextFor(sourceId, key, args);
		}

		public static string GetText(string key, params object[] args)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			if (string.IsNullOrEmpty(key))
			{
				return "<key_not_set>";
			}
			string text = TryGetRegisteredText(key, CurrentLocale) ?? TryGetGameText(key) ?? key;
			if (args == null || args.Length == 0)
			{
				return text;
			}
			try
			{
				return string.Format(text, args);
			}
			catch (Exception ex)
			{
				ManualLogSource logger = RomesteadLocalizationAPI.Logger;
				if (logger != null)
				{
					bool flag = default(bool);
					BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(39, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Faulty localization format string at ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(key);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
					}
					logger.LogWarning(val);
				}
				return text;
			}
		}

		public static string GetTextFor(string sourceId, string key, params object[] args)
		{
			ValidateSourceId(sourceId);
			if (string.IsNullOrEmpty(key))
			{
				return "<key_not_set>";
			}
			return GetText(ScopeKey(sourceId, key), args);
		}

		public static void Apply()
		{
			Apply(DetectCurrentLocale(), notify: false);
		}

		public static IReadOnlyList<string> GetSupportedLocales()
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			string[] known = RomesteadLocales.Known;
			foreach (string item in known)
			{
				hashSet.Add(item);
			}
			try
			{
				foreach (string key in Globals.SupportedLanguageCodes.Keys)
				{
					if (!string.Equals(key, "<testing>", StringComparison.OrdinalIgnoreCase))
					{
						hashSet.Add(NormalizeLocale(key));
					}
				}
			}
			catch
			{
			}
			foreach (string contentLocale in GetContentLocales())
			{
				hashSet.Add(contentLocale);
			}
			List<string> list = new List<string>(hashSet);
			list.Sort(StringComparer.OrdinalIgnoreCase);
			return list;
		}

		public static string NormalizeLocale(string locale)
		{
			string text = (locale ?? string.Empty).Trim().Replace('-', '_');
			if (text != null)
			{
				int length = text.Length;
				if (length <= 2)
				{
					if (length == 0)
					{
						return "en";
					}
					if (length == 2)
					{
						switch (text[0])
						{
						case 'e':
							break;
						case 'z':
							goto IL_0170;
						case 'c':
							if (!(text == "cs"))
							{
								goto IL_03fb;
							}
							return "cs_CZ";
						case 'd':
							if (!(text == "de"))
							{
								goto IL_03fb;
							}
							return "de_DE";
						case 'f':
							if (!(text == "fr"))
							{
								goto IL_03fb;
							}
							return "fr_FR";
						case 'i':
							if (!(text == "it"))
							{
								goto IL_03fb;
							}
							return "it_IT";
						case 'j':
							if (!(text == "ja"))
							{
								goto IL_03fb;
							}
							return "ja_JP";
						case 'k':
							if (!(text == "ko"))
							{
								goto IL_03fb;
							}
							return "ko_KR";
						case 'n':
							if (!(text == "nl"))
							{
								goto IL_03fb;
							}
							return "nl_NL";
						case 'p':
							goto IL_0218;
						case 'r':
							if (!(text == "ru"))
							{
								goto IL_03fb;
							}
							return "ru_RU";
						case 's':
							goto IL_0252;
						case 't':
							goto IL_0277;
						case 'u':
							if (!(text == "uk"))
							{
								goto IL_03fb;
							}
							return "uk_UA";
						default:
							goto IL_03fb;
						}
						switch (text)
						{
						case "en":
							break;
						case "el":
							return "el_GR";
						case "es":
							return "es_ES";
						default:
							goto IL_03fb;
						}
						goto IL_037d;
					}
				}
				else if (length != 5)
				{
					if (length == 7)
					{
						char c = text[6];
						if (c != 's')
						{
							if (c == 't' && text == "zh_Hant")
							{
								goto IL_0389;
							}
						}
						else if (text == "zh_Hans")
						{
							goto IL_0383;
						}
					}
				}
				else
				{
					switch (text[3])
					{
					case 'U':
						break;
					case 'G':
						goto IL_02c6;
					case 'C':
						goto IL_02db;
					case 'T':
						goto IL_02f0;
					case 'H':
						goto IL_0305;
					case 'M':
						goto IL_0317;
					case 'B':
						goto IL_0329;
					case 'P':
						if (!(text == "pt_PT"))
						{
							goto IL_03fb;
						}
						return "pt_PT";
					default:
						goto IL_03fb;
					}
					if (text == "en_US")
					{
						goto IL_037d;
					}
				}
			}
			goto IL_03fb;
			IL_0252:
			if (!(text == "sk"))
			{
				if (!(text == "sv"))
				{
					goto IL_03fb;
				}
				return "sv_SE";
			}
			return "sk_SK";
			IL_0218:
			if (!(text == "pl"))
			{
				if (text == "pt")
				{
					goto IL_03cb;
				}
				goto IL_03fb;
			}
			return "pl_PL";
			IL_0170:
			if (text == "zh")
			{
				goto IL_0383;
			}
			goto IL_03fb;
			IL_0389:
			return "zh_TW";
			IL_0383:
			return "zh_CN";
			IL_0277:
			if (!(text == "th"))
			{
				if (!(text == "tr"))
				{
					goto IL_03fb;
				}
				return "tr_TR";
			}
			return "th_TH";
			IL_02c6:
			if (text == "en_GB")
			{
				goto IL_037d;
			}
			goto IL_03fb;
			IL_02db:
			if (text == "zh_CN")
			{
				goto IL_0383;
			}
			goto IL_03fb;
			IL_02f0:
			if (text == "zh_TW")
			{
				goto IL_0389;
			}
			goto IL_03fb;
			IL_0305:
			if (text == "zh_HK")
			{
				goto IL_0389;
			}
			goto IL_03fb;
			IL_0317:
			if (text == "zh_MO")
			{
				goto IL_0389;
			}
			goto IL_03fb;
			IL_0329:
			if (text == "pt_BR")
			{
				goto IL_03cb;
			}
			goto IL_03fb;
			IL_03fb:
			return text;
			IL_037d:
			return "en";
			IL_03cb:
			return "pt_BR";
		}

		internal static void OnGameLocalizationLoaded(string locale)
		{
			Apply(NormalizeLocale(locale), notify: true);
		}

		private static void RegisterBundle(string sourceId, Dictionary<string, Dictionary<string, string>> bundle)
		{
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			lock (Sync)
			{
				bool flag = default(bool);
				foreach (KeyValuePair<string, Dictionary<string, string>> item in bundle)
				{
					string key = NormalizeLocale(item.Key);
					foreach (KeyValuePair<string, string> item2 in item.Value)
					{
						string text = ScopeKey(sourceId, item2.Key);
						if (!Entries.TryGetValue(text, out var value))
						{
							value = new Entry(sourceId);
							Entries[text] = value;
						}
						else if (!string.Equals(value.SourceId, sourceId, StringComparison.Ordinal))
						{
							ManualLogSource logger = RomesteadLocalizationAPI.Logger;
							if (logger != null)
							{
								BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(35, 3, ref flag);
								if (flag)
								{
									((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Localization key ");
									((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text);
									((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" from ");
									((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(sourceId);
									((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" overrides ");
									((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(value.SourceId);
									((BepInExLogInterpolatedStringHandler)val).AppendLiteral(".");
								}
								logger.LogWarning(val);
							}
							value.SourceId = sourceId;
						}
						value.Texts[key] = item2.Value;
					}
				}
			}
			Apply();
		}

		private static void Apply(string locale, bool notify)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			string text = NormalizeLocale(locale);
			bool flag = false;
			lock (Sync)
			{
				flag = !string.Equals(_currentLocale, text, StringComparison.OrdinalIgnoreCase);
				_currentLocale = text;
				if (string.Equals(text, "<testing>", StringComparison.OrdinalIgnoreCase))
				{
					return;
				}
				foreach (KeyValuePair<string, Entry> entry in Entries)
				{
					string text2 = SelectText(entry.Value.Texts, text);
					if (text2 != null)
					{
						StringDefinitions.Strings[entry.Key] = new StringDefinition(text2);
					}
				}
			}
			if (notify)
			{
				RomesteadLocalization.LocalizationApplied?.Invoke(text);
				if (flag)
				{
					RomesteadLocalization.LanguageChanged?.Invoke(text);
				}
			}
		}

		private static string TryGetRegisteredText(string key, string locale)
		{
			lock (Sync)
			{
				Entry value;
				return Entries.TryGetValue(key, out value) ? SelectText(value.Texts, locale) : null;
			}
		}

		private static string TryGetGameText(string key)
		{
			try
			{
				string text = default(string);
				return StringDefinitions.TryGetString(key, ref text) ? text : null;
			}
			catch
			{
				return null;
			}
		}

		private static string SelectText(Dictionary<string, string> texts, string locale)
		{
			string text = NormalizeLocale(locale);
			if (string.Equals(text, "<testing>", StringComparison.OrdinalIgnoreCase))
			{
				return null;
			}
			if (texts.TryGetValue(text, out var value))
			{
				return value;
			}
			if (texts.TryGetValue("en", out var value2))
			{
				return value2;
			}
			if (texts.TryGetValue("zh_CN", out var value3))
			{
				return value3;
			}
			using (Dictionary<string, string>.ValueCollection.Enumerator enumerator = texts.Values.GetEnumerator())
			{
				if (enumerator.MoveNext())
				{
					return enumerator.Current;
				}
			}
			return null;
		}

		private static string DetectCurrentLocale()
		{
			if (!string.IsNullOrWhiteSpace(StringDefinitions.CurrentLanguageCode))
			{
				return NormalizeLocale(StringDefinitions.CurrentLanguageCode);
			}
			try
			{
				if (!string.IsNullOrWhiteSpace(Globals.LanguageCode))
				{
					return NormalizeLocale(Globals.LanguageCode);
				}
			}
			catch
			{
			}
			string text = ReadSavedLocale();
			if (!string.IsNullOrWhiteSpace(text))
			{
				return NormalizeLocale(text);
			}
			string text2 = NormalizeLocale(CultureInfo.CurrentCulture.Name);
			string[] known = RomesteadLocales.Known;
			foreach (string b in known)
			{
				if (string.Equals(text2, b, StringComparison.OrdinalIgnoreCase))
				{
					return text2;
				}
			}
			return _currentLocale;
		}

		private static string ReadSavedLocale()
		{
			try
			{
				string path = Path.Combine(GamePathHelper.GetFolderPath((GameFolder)1, false), "localization.json");
				if (!File.Exists(path))
				{
					return null;
				}
				using FileStream utf8Json = File.OpenRead(path);
				using JsonDocument jsonDocument = JsonDocument.Parse((Stream)utf8Json, new JsonDocumentOptions
				{
					AllowTrailingCommas = true,
					CommentHandling = JsonCommentHandling.Skip
				});
				JsonElement value;
				return (jsonDocument.RootElement.TryGetProperty("LanguageCode", out value) && value.ValueKind == JsonValueKind.String) ? value.GetString() : null;
			}
			catch
			{
				return null;
			}
		}

		private static IEnumerable<string> GetContentLocales()
		{
			string text = null;
			try
			{
				object obj = typeof(Globals).GetField("ContentManager")?.GetValue(null);
				text = obj?.GetType().GetProperty("RootDirectory")?.GetValue(obj) as string;
			}
			catch
			{
			}
			if (string.IsNullOrWhiteSpace(text))
			{
				yield break;
			}
			string path = Path.Combine(text, "localization");
			if (!Directory.Exists(path))
			{
				yield break;
			}
			string[] files = Directory.GetFiles(path, "locale_*");
			for (int i = 0; i < files.Length; i++)
			{
				string fileName = Path.GetFileName(files[i]);
				if (fileName != null && fileName.Length > "locale_".Length)
				{
					yield return NormalizeLocale(fileName.Substring("locale_".Length));
				}
			}
		}

		private static Dictionary<string, string> ReadJsonTextMap(string path)
		{
			if (string.IsNullOrWhiteSpace(path))
			{
				throw new ArgumentException("Localization file path cannot be empty.", "path");
			}
			if (!File.Exists(path))
			{
				throw new FileNotFoundException("Localization file was not found.", path);
			}
			using FileStream utf8Json = File.OpenRead(path);
			using JsonDocument jsonDocument = JsonDocument.Parse((Stream)utf8Json, new JsonDocumentOptions
			{
				AllowTrailingCommas = true,
				CommentHandling = JsonCommentHandling.Skip
			});
			if (jsonDocument.RootElement.ValueKind != JsonValueKind.Object)
			{
				throw new JsonException("Localization file must contain a JSON object: " + path);
			}
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			foreach (JsonProperty item in jsonDocument.RootElement.EnumerateObject())
			{
				if (item.Value.ValueKind != JsonValueKind.String)
				{
					throw new JsonException("Localization value must be a string: " + path + " -> " + item.Name);
				}
				dictionary[item.Name] = item.Value.GetString();
			}
			return dictionary;
		}

		private static Dictionary<string, Dictionary<string, string>> ReadJsonBundle(string path)
		{
			if (string.IsNullOrWhiteSpace(path))
			{
				throw new ArgumentException("Localization bundle path cannot be empty.", "path");
			}
			if (!File.Exists(path))
			{
				throw new FileNotFoundException("Localization bundle was not found.", path);
			}
			using FileStream utf8Json = File.OpenRead(path);
			using JsonDocument jsonDocument = JsonDocument.Parse((Stream)utf8Json, new JsonDocumentOptions
			{
				AllowTrailingCommas = true,
				CommentHandling = JsonCommentHandling.Skip
			});
			if (jsonDocument.RootElement.ValueKind != JsonValueKind.Object)
			{
				throw new JsonException("Localization bundle must contain a JSON object: " + path);
			}
			Dictionary<string, Dictionary<string, string>> result = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
			foreach (JsonProperty item in jsonDocument.RootElement.EnumerateObject())
			{
				if (!IsLocaleName(item.Name))
				{
					throw new JsonException("Localization bundle top-level key must be a locale: " + path + " -> " + item.Name);
				}
				if (item.Value.ValueKind != JsonValueKind.Object)
				{
					throw new JsonException("Localization locale value must be an object: " + path + " -> " + item.Name);
				}
				foreach (JsonProperty item2 in item.Value.EnumerateObject())
				{
					AddBundleText(result, item.Name, item2.Name, item2.Value, path);
				}
			}
			return result;
		}

		private static string GetLocaleFromJsonFile(string path)
		{
			string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
			if (fileNameWithoutExtension == null)
			{
				return "en";
			}
			if (!fileNameWithoutExtension.StartsWith("locale_", StringComparison.OrdinalIgnoreCase))
			{
				return NormalizeLocale(fileNameWithoutExtension);
			}
			return NormalizeLocale(fileNameWithoutExtension.Substring("locale_".Length));
		}

		private static bool IsLocaleName(string value)
		{
			string text = NormalizeLocale(value);
			string[] known = RomesteadLocales.Known;
			foreach (string b in known)
			{
				if (string.Equals(text, b, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			if (text.Length != 2)
			{
				if (text.Length >= 5)
				{
					return text[2] == '_';
				}
				return false;
			}
			return true;
		}

		private static void AddBundleText(Dictionary<string, Dictionary<string, string>> result, string locale, string key, JsonElement value, string path)
		{
			if (value.ValueKind != JsonValueKind.String)
			{
				throw new JsonException($"Localization value must be a string: {path} -> {locale}.{key}");
			}
			AddBundleText(result, locale, key, value.GetString());
		}

		private static void AddBundleText(Dictionary<string, Dictionary<string, string>> result, string locale, string key, string text)
		{
			if (text == null)
			{
				throw new ArgumentNullException("text");
			}
			string key2 = NormalizeLocale(locale);
			if (!result.TryGetValue(key2, out var value))
			{
				value = (result[key2] = new Dictionary<string, string>(StringComparer.Ordinal));
			}
			value[key] = text;
		}

		private static string ScopeKey(string sourceId, string key)
		{
			if (!key.StartsWith(sourceId + ".", StringComparison.Ordinal))
			{
				return sourceId + "." + key;
			}
			return key;
		}

		private static void ValidateSourceId(string sourceId)
		{
			if (string.IsNullOrWhiteSpace(sourceId))
			{
				throw new ArgumentException("Source id cannot be empty.", "sourceId");
			}
		}

		private static void ValidateLocale(string locale)
		{
			if (string.IsNullOrWhiteSpace(locale))
			{
				throw new ArgumentException("Locale cannot be empty.", "locale");
			}
		}

		private static void ValidateKey(string key)
		{
			if (string.IsNullOrWhiteSpace(key))
			{
				throw new ArgumentException("Localization key cannot be empty.", "key");
			}
		}
	}
	public sealed class RomesteadLocalizer
	{
		public string SourceId { get; }

		public string CurrentLocale => RomesteadLocalization.CurrentLocale;

		public event Action<string> LanguageChanged
		{
			add
			{
				RomesteadLocalization.LanguageChanged += value;
			}
			remove
			{
				RomesteadLocalization.LanguageChanged -= value;
			}
		}

		internal RomesteadLocalizer(string sourceId)
		{
			SourceId = sourceId;
		}

		public RomesteadLocalizer RegisterJson(string path)
		{
			RomesteadLocalization.RegisterJson(SourceId, path);
			return this;
		}

		public RomesteadLocalizer RegisterJsonDirectory(string directory)
		{
			RomesteadLocalization.RegisterJsonDirectory(SourceId, directory);
			return this;
		}

		public RomesteadLocalizer RegisterText(string locale, string key, string text)
		{
			RomesteadLocalization.RegisterText(SourceId, locale, key, text);
			return this;
		}

		public RomesteadLocalizer RegisterTexts(string locale, IReadOnlyDictionary<string, string> texts)
		{
			RomesteadLocalization.RegisterTexts(SourceId, locale, texts);
			return this;
		}

		public RomesteadLocalizer RegisterTexts(params (string Locale, string Key, string Text)[] texts)
		{
			RomesteadLocalization.RegisterTexts(SourceId, texts);
			return this;
		}

		public string Text(string key, params object[] args)
		{
			return RomesteadLocalization.TextFor(SourceId, key, args);
		}

		public string GetText(string key, params object[] args)
		{
			return RomesteadLocalization.GetTextFor(SourceId, key, args);
		}
	}
}