using System; using System.Collections.Generic; using System.Reflection; namespace PKHeX.Core; /// /// Localization utility for changing all properties of a static type. /// public static class LocalizationUtil { private const string TranslationSplitter = " = "; private const char TranslationFirst = ' '; // perf; no properties contain spaces. /// /// Gets the names of the properties defined in the given input /// /// Enumerable of translation definitions in the form "Property = Value". private static string[] GetProperties(ReadOnlySpan input) { var result = new string[input.Length]; for (int i = 0; i < input.Length; i++) { var l = input[i]; var split = l.IndexOf(TranslationFirst); result[i] = l[..split]; } return result; } private static string[] DumpStrings(Type t) { var ti = t.GetTypeInfo(); var props = (PropertyInfo[])ti.DeclaredProperties; var result = new string[props.Length]; for (int i = 0; i < props.Length; i++) { var p = props[i]; var value = p.GetValue(null); result[i] = $"{p.Name}{TranslationSplitter}{value}"; } return result; } /// /// Gets the current localization in a static class containing language-specific strings /// /// public static string[] GetLocalization(Type t) => DumpStrings(t); /// /// Gets the current localization in a static class containing language-specific strings /// /// /// Existing localization lines (if provided) public static string[] GetLocalization(Type t, ReadOnlySpan existingLines) { var currentLines = GetLocalization(t); var existing = GetProperties(existingLines); var current = GetProperties(currentLines); var result = new string[currentLines.Length]; for (int i = 0; i < current.Length; i++) { int index = Array.IndexOf(existing, current[i]); result[i] = index < 0 ? currentLines[i] : existingLines[index]; } return result; } /// /// Applies localization to a static class containing language-specific strings. /// /// Type of the static class containing the desired strings. /// Lines containing the localized strings private static void SetLocalization(Type t, ReadOnlySpan lines) { if (lines.Length == 0) return; var translatable = new Dictionary(lines.Length); foreach (var line in lines) { var index = line.IndexOf(TranslationFirst); if (index < 0) continue; var prop = line[..index]; translatable[prop] = line[(index + TranslationSplitter.Length)..]; } var ti = t.GetTypeInfo(); var props = (PropertyInfo[])ti.DeclaredProperties; foreach (var p in props) { if (translatable.TryGetValue(p.Name, out var value)) p.SetValue(null, value); } } /// /// Applies localization to a static class containing language-specific strings. /// /// Type of the static class containing the desired strings. /// Prefix of the language file to use. Example: if the target is legality_en.txt, should be "legality". /// Culture information private static void SetLocalization(Type t, string languageFilePrefix, string currentCultureCode) { var lines = Util.GetStringList($"{languageFilePrefix}_{currentCultureCode}"); SetLocalization(t, lines); } /// /// Applies localization to a static class containing language-specific strings. /// /// Type of the static class containing the desired strings. /// The values used to translate the given static class are retrieved from [TypeName]_[CurrentLangCode2].txt in the resource manager of PKHeX.Core. /// Culture information public static void SetLocalization(Type t, string currentCultureCode) { SetLocalization(t, t.Name, currentCultureCode); } }