diff --git a/Core/TimberApi/LocalizationSystem/LocalizationFetcher.cs b/Core/TimberApi/LocalizationSystem/LocalizationFetcher.cs index 6a54299a..7f6e94b2 100644 --- a/Core/TimberApi/LocalizationSystem/LocalizationFetcher.cs +++ b/Core/TimberApi/LocalizationSystem/LocalizationFetcher.cs @@ -6,6 +6,7 @@ using LINQtoCSV; using TimberApi.DependencyContainerSystem; using TimberApi.ModSystem; +using Timberborn.Common; using Timberborn.Localization; using UnityEngine; @@ -20,98 +21,88 @@ internal static class LocalizationFetcher /// public static Dictionary GetLocalization(string localizationKey) { - var localizedRecords = GetLocalizationRecordsFromFiles(localizationKey, GetLocalizationFilePathsFromDependencies(localizationKey)) - .ToDictionary(record => record.Id, record => TextColors.ColorizeText(record.Text)); - - foreach (LocalizationRecord defaultRecord in GetDefaultLocalization()) + if (localizationKey == LocalizationCodes.Default) { - var id = defaultRecord.Id; - if (!localizedRecords.TryGetValue(id, out string text) || string.IsNullOrEmpty(text)) - { - localizedRecords[id] = TextColors.ColorizeText(defaultRecord.Text); - } + return GetLocalizationRecordsFromFiles(localizationKey, GetLocalizationFilePathsFromDependencies(localizationKey)) + .ToDictionary(record => record.Id, record => TextColors.ColorizeText(record.Text))!; } - return localizedRecords; + var userLocalizationRecords = GetLocalizationRecordsFromFiles(localizationKey, GetLocalizationFilePathsFromDependencies(localizationKey)); + var defaultLocalizationRecords = GetLocalizationRecordsFromFiles(LocalizationCodes.Default, GetLocalizationFilePathsFromDependencies(LocalizationCodes.Default)); + + return defaultLocalizationRecords + .ToDictionary( + defaultLocalizationRecord => defaultLocalizationRecord.Id, + defaultLocalizationRecord => TextColors.ColorizeText(userLocalizationRecords.FirstOrDefault(userLocalizationRecord => userLocalizationRecord.Id == defaultLocalizationRecord.Id)?.Text ?? defaultLocalizationRecord.Text) + )!; } /// /// Parses text files into LocalizationRecords /// - /// + /// /// /// - private static IEnumerable GetLocalizationRecordsFromFiles(string localization, IEnumerable filePaths) + private static IEnumerable GetLocalizationRecordsFromFiles(string localizationKey, IEnumerable filePaths) { - List records = new(); - foreach (LocalizationFile localizationFile in filePaths) - { - records.AddRange(TryToReadRecords(localization, localizationFile)); - } - - return records; + return filePaths + .SelectMany(localizationFile => TryToReadRecords(localizationKey, localizationFile)) + .GroupBy(record => record.Id) + .Select(recordsGroupedById => recordsGroupedById.Last()) + .ToList(); } /// /// Timberborn method Timberborn.Localization.LocalizationRepository.TryToReadRecords /// - /// + /// /// /// /// - private static IEnumerable TryToReadRecords(string localization, LocalizationFile localizationFile) + private static IEnumerable TryToReadRecords(string localizationKey, LocalizationFile localizationFile) { try { - var localizationRecords = new CsvContext().Read(localizationFile.FilePath); - - ValidateLocalizationRecords(localizationRecords, localizationFile.Mod); - - return new CsvContext().Read(localizationFile.FilePath); + return CleanLocalizationRecords(new CsvContext().Read(localizationFile.FilePath), localizationFile.Mod); } - catch (Exception ex) + catch (Exception exception) { - var message = "Unable to parse file for " + localization + "."; - if (ex is AggregatedException aggregatedException) + var message = "Unable to parse file for " + localizationKey + "."; + if (exception is AggregatedException aggregatedException) { message = message + " First error: " + aggregatedException.m_InnerExceptionsList[0].Message; } - if (localization == LocalizationCodes.Default) + if (localizationKey == LocalizationCodes.Default) { - throw new InvalidDataException(message, ex); + throw new InvalidDataException(message, exception); } TimberApi.ConsoleWriter.Log(message, LogType.Error); + return new List(); } } - private static void ValidateLocalizationRecords(IEnumerable localizationRecords, IMod mod) + private static IEnumerable CleanLocalizationRecords(IEnumerable localizationRecords, IMod mod) { - var hasValidationErrors = false; + var records = localizationRecords.ToList(); + + var errors = records + .Where(record => record.Text is null) + .ToList(); - foreach (var record in localizationRecords) + if (errors.IsEmpty()) { - if(record.Text is null) - { - hasValidationErrors = true; - TimberApi.ConsoleWriter.LogAs(mod.Name, $"Localization Id does not have any text: {record.Id}", LogType.Error); - } + return records.Where(record => record.Id is not null); } - if(hasValidationErrors) + foreach (var error in errors) { - throw new Exception($"Validating localization files for {mod.Name} failed."); + TimberApi.ConsoleWriter.LogAs(mod.Name, $"Localization Id does not have any text: {error.Id}", LogType.Error); } - } - /// - /// Returns the default localization - /// - private static IEnumerable GetDefaultLocalization() - { - return GetLocalizationRecordsFromFiles(LocalizationCodes.Default, GetLocalizationFilePathsFromDependencies(LocalizationCodes.Default)); + throw new Exception($"Validating localization files for {mod.Name} failed."); } /// @@ -121,53 +112,28 @@ private static IEnumerable GetDefaultLocalization() /// private static IEnumerable GetLocalizationFilePathsFromDependencies(string localizationKey) { - List localizationFilePaths = new(); - foreach (IMod mod in DependencyContainer.GetInstance().All()) - { - var pluginLocalizationPath = Path.Combine(mod.DirectoryPath, mod.LanguagePath); - - (var hasLocalization, var localizationName) = LocalizationNameOrDefault(pluginLocalizationPath, localizationKey); - - if (!hasLocalization) - { - continue; - } - - localizationFilePaths.Add(new LocalizationFile(mod, Path.Combine(pluginLocalizationPath, localizationName))); - } - - return localizationFilePaths; + return ( + from mod in DependencyContainer.GetInstance().All() + let pluginLocalizationPath = Path.Combine(mod.DirectoryPath, mod.LanguagePath) + let localizationFile = GetLocalizationFile(pluginLocalizationPath, localizationKey) ?? GetLocalizationFile(pluginLocalizationPath, LocalizationCodes.Default) + where localizationFile is not null + select new LocalizationFile(mod, Path.Combine(pluginLocalizationPath, localizationFile)) + ).ToList(); } /// - /// Check if localization file exists, return default if not - /// Returns false if default and localization file doesn't exists + /// Get the localization file from the plugin localization path or return null /// /// - /// - private static (bool, string) LocalizationNameOrDefault(string pluginLocalizationPath, string localizationName) + /// + private static string? GetLocalizationFile(string pluginLocalizationPath, string localizationKey) { - if (string.IsNullOrEmpty(localizationName)) - { - return (false, ""); - } - - if (!Directory.Exists(pluginLocalizationPath)) - { - return (false, ""); - } - - if (File.Exists(Path.Combine(pluginLocalizationPath, localizationName + ".txt"))) - { - return (true, localizationName + ".txt"); - } - - if (File.Exists(Path.Combine(pluginLocalizationPath, LocalizationCodes.Default + ".txt"))) + if (string.IsNullOrEmpty(localizationKey) || !Directory.Exists(pluginLocalizationPath)) { - return (true, LocalizationCodes.Default + ".txt"); + return null; } - return (false, ""); + return File.Exists(Path.Combine(pluginLocalizationPath, localizationKey + ".txt")) ? localizationKey + ".txt" : null; } } -} +} \ No newline at end of file diff --git a/Core/TimberApi/LocalizationSystem/LocalizationPatcher.cs b/Core/TimberApi/LocalizationSystem/LocalizationPatcher.cs index 4a4ec2cc..6c4b403b 100644 --- a/Core/TimberApi/LocalizationSystem/LocalizationPatcher.cs +++ b/Core/TimberApi/LocalizationSystem/LocalizationPatcher.cs @@ -1,10 +1,7 @@ -using System; using System.Collections.Generic; using HarmonyLib; using TimberApi.HarmonyPatcherSystem; -using Timberborn.Common; using Timberborn.Localization; -using UnityEngine; namespace TimberApi.LocalizationSystem { @@ -23,15 +20,12 @@ public override void Apply(Harmony harmony) public static void GetLocalizationPatch(string localizationKey, ref IDictionary __result) { IDictionary localization = LocalizationFetcher.GetLocalization(localizationKey); - try - { - __result.AddRange(localization); - TimberApi.ConsoleWriter.Log($"Loaded {localization.Count} custom labels"); - } - catch (Exception e) + + TimberApi.ConsoleWriter.Log($"Loaded {localization.Count} custom labels"); + + foreach (var (key, value) in localization) { - TimberApi.ConsoleWriter.Log(e.ToString(), LogType.Error); - throw; + __result[key] = value; } } } diff --git a/Core/TimberApi/LocalizationSystem/LocalizationRecord.cs b/Core/TimberApi/LocalizationSystem/LocalizationRecord.cs index 4b1cc20a..e94490fb 100644 --- a/Core/TimberApi/LocalizationSystem/LocalizationRecord.cs +++ b/Core/TimberApi/LocalizationSystem/LocalizationRecord.cs @@ -8,13 +8,13 @@ namespace TimberApi.LocalizationSystem internal class LocalizationRecord { [CsvColumn(Name = "ID")] - public string Id { get; set; } = null!; + public string? Id { get; set; } = null; [CsvColumn(Name = "Text")] - public string Text { get; set; } = null!; + public string? Text { get; set; } = null; [CsvColumn(Name = "Comment")] - public string Comment { get; set; } = null!; + public string? Comment { get; set; } = null; public bool IsWip { get; set; } }