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("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("Romestead Localization API"); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(" initialization error: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex.Message); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("\n"); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(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 Texts = new Dictionary(StringComparer.OrdinalIgnoreCase); public Entry(string sourceId) { SourceId = sourceId; } } private const string TestingLocale = ""; private static readonly object Sync = new object(); private static readonly Dictionary Entries = new Dictionary(StringComparer.Ordinal); private static string _currentLocale = "en"; public static string CurrentLocale => DetectCurrentLocale(); public static event Action LanguageChanged; public static event Action 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> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); string[] files = Directory.GetFiles(directory, "*.json", SearchOption.TopDirectoryOnly); Array.Sort(files, (IComparer?)StringComparer.OrdinalIgnoreCase); string[] array = files; foreach (string path in array) { string localeFromJsonFile = GetLocaleFromJsonFile(path); foreach (KeyValuePair 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> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); AddBundleText(dictionary, locale, key, text); RegisterBundle(sourceId, dictionary); } public static void RegisterTexts(string sourceId, string locale, IReadOnlyDictionary texts) { ValidateSourceId(sourceId); ValidateLocale(locale); if (texts == null) { throw new ArgumentNullException("texts"); } Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair 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> dictionary = new Dictionary>(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 ""; } 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(key); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(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 ""; } return GetText(ScopeKey(sourceId, key), args); } public static void Apply() { Apply(DetectCurrentLocale(), notify: false); } public static IReadOnlyList GetSupportedLocales() { HashSet hashSet = new HashSet(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, "", StringComparison.OrdinalIgnoreCase)) { hashSet.Add(NormalizeLocale(key)); } } } catch { } foreach (string contentLocale in GetContentLocales()) { hashSet.Add(contentLocale); } List list = new List(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> 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> item in bundle) { string key = NormalizeLocale(item.Key); foreach (KeyValuePair 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(text); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" from "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(sourceId); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" overrides "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(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, "", StringComparison.OrdinalIgnoreCase)) { return; } foreach (KeyValuePair 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 texts, string locale) { string text = NormalizeLocale(locale); if (string.Equals(text, "", 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.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 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 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 dictionary = new Dictionary(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> 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> result = new Dictionary>(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> 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> 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(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 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 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); } } }