From 332bcfa5a19509352aa417a04a677b5701c16986 Mon Sep 17 00:00:00 2001
From: Jesse Plamondon-Willard <Pathoschild@users.noreply.github.com>
Date: Thu, 21 Mar 2019 23:12:26 -0400
Subject: add content pack translations

---
 src/SMAPI/Framework/ContentPack.cs            |  7 +++++-
 src/SMAPI/Framework/IModMetadata.cs           | 10 ++++++--
 src/SMAPI/Framework/ModLoading/ModMetadata.cs | 12 ++++++++--
 src/SMAPI/Framework/SCore.cs                  | 33 +++++++++++++--------------
 4 files changed, 40 insertions(+), 22 deletions(-)

(limited to 'src/SMAPI/Framework')

diff --git a/src/SMAPI/Framework/ContentPack.cs b/src/SMAPI/Framework/ContentPack.cs
index 5384d98f..829a7dc1 100644
--- a/src/SMAPI/Framework/ContentPack.cs
+++ b/src/SMAPI/Framework/ContentPack.cs
@@ -30,6 +30,9 @@ namespace StardewModdingAPI.Framework
         /// <summary>The content pack's manifest.</summary>
         public IManifest Manifest { get; }
 
+        /// <summary>Provides translations stored in the content pack's <c>i18n</c> folder. See <see cref="IModHelper.Translation"/> for more info.</summary>
+        public ITranslationHelper Translation { get; }
+
 
         /*********
         ** Public methods
@@ -38,12 +41,14 @@ namespace StardewModdingAPI.Framework
         /// <param name="directoryPath">The full path to the content pack's folder.</param>
         /// <param name="manifest">The content pack's manifest.</param>
         /// <param name="content">Provides an API for loading content assets.</param>
+        /// <param name="translation">Provides translations stored in the content pack's <c>i18n</c> folder.</param>
         /// <param name="jsonHelper">Encapsulates SMAPI's JSON file parsing.</param>
-        public ContentPack(string directoryPath, IManifest manifest, IContentHelper content, JsonHelper jsonHelper)
+        public ContentPack(string directoryPath, IManifest manifest, IContentHelper content, ITranslationHelper translation, JsonHelper jsonHelper)
         {
             this.DirectoryPath = directoryPath;
             this.Manifest = manifest;
             this.Content = content;
+            this.Translation = translation;
             this.JsonHelper = jsonHelper;
         }
 
diff --git a/src/SMAPI/Framework/IModMetadata.cs b/src/SMAPI/Framework/IModMetadata.cs
index 38514959..32870c2a 100644
--- a/src/SMAPI/Framework/IModMetadata.cs
+++ b/src/SMAPI/Framework/IModMetadata.cs
@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+using StardewModdingAPI.Framework.ModHelpers;
 using StardewModdingAPI.Framework.ModLoading;
 using StardewModdingAPI.Toolkit.Framework.Clients.WebApi;
 using StardewModdingAPI.Toolkit.Framework.ModData;
@@ -42,6 +43,9 @@ namespace StardewModdingAPI.Framework
         /// <summary>The content pack instance (if loaded and <see cref="IModInfo.IsContentPack"/> is true).</summary>
         IContentPack ContentPack { get; }
 
+        /// <summary>The translations for this mod (if loaded).</summary>
+        TranslationHelper Translations { get; }
+
         /// <summary>Writes messages to the console and log file as this mod.</summary>
         IMonitor Monitor { get; }
 
@@ -67,12 +71,14 @@ namespace StardewModdingAPI.Framework
 
         /// <summary>Set the mod instance.</summary>
         /// <param name="mod">The mod instance to set.</param>
-        IModMetadata SetMod(IMod mod);
+        /// <param name="translations">The translations for this mod (if loaded).</param>
+        IModMetadata SetMod(IMod mod, TranslationHelper translations);
 
         /// <summary>Set the mod instance.</summary>
         /// <param name="contentPack">The contentPack instance to set.</param>
         /// <param name="monitor">Writes messages to the console and log file.</param>
-        IModMetadata SetMod(IContentPack contentPack, IMonitor monitor);
+        /// <param name="translations">The translations for this mod (if loaded).</param>
+        IModMetadata SetMod(IContentPack contentPack, IMonitor monitor, TranslationHelper translations);
 
         /// <summary>Set the mod-provided API instance.</summary>
         /// <param name="api">The mod-provided API.</param>
diff --git a/src/SMAPI/Framework/ModLoading/ModMetadata.cs b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
index 4ff021b7..39f2f482 100644
--- a/src/SMAPI/Framework/ModLoading/ModMetadata.cs
+++ b/src/SMAPI/Framework/ModLoading/ModMetadata.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using StardewModdingAPI.Framework.ModHelpers;
 using StardewModdingAPI.Toolkit.Framework.Clients.WebApi;
 using StardewModdingAPI.Toolkit.Framework.ModData;
 using StardewModdingAPI.Toolkit.Framework.UpdateData;
@@ -46,6 +47,9 @@ namespace StardewModdingAPI.Framework.ModLoading
         /// <summary>The content pack instance (if loaded and <see cref="IsContentPack"/> is true).</summary>
         public IContentPack ContentPack { get; private set; }
 
+        /// <summary>The translations for this mod (if loaded).</summary>
+        public TranslationHelper Translations { get; private set; }
+
         /// <summary>Writes messages to the console and log file as this mod.</summary>
         public IMonitor Monitor { get; private set; }
 
@@ -100,26 +104,30 @@ namespace StardewModdingAPI.Framework.ModLoading
 
         /// <summary>Set the mod instance.</summary>
         /// <param name="mod">The mod instance to set.</param>
-        public IModMetadata SetMod(IMod mod)
+        /// <param name="translations">The translations for this mod (if loaded).</param>
+        public IModMetadata SetMod(IMod mod, TranslationHelper translations)
         {
             if (this.ContentPack != null)
                 throw new InvalidOperationException("A mod can't be both an assembly mod and content pack.");
 
             this.Mod = mod;
             this.Monitor = mod.Monitor;
+            this.Translations = translations;
             return this;
         }
 
         /// <summary>Set the mod instance.</summary>
         /// <param name="contentPack">The contentPack instance to set.</param>
         /// <param name="monitor">Writes messages to the console and log file.</param>
-        public IModMetadata SetMod(IContentPack contentPack, IMonitor monitor)
+        /// <param name="translations">The translations for this mod (if loaded).</param>
+        public IModMetadata SetMod(IContentPack contentPack, IMonitor monitor, TranslationHelper translations)
         {
             if (this.Mod != null)
                 throw new InvalidOperationException("A mod can't be both an assembly mod and content pack.");
 
             this.ContentPack = contentPack;
             this.Monitor = monitor;
+            this.Translations = translations;
             return this;
         }
 
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index 06a2e0af..2f0e0f05 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -427,8 +427,8 @@ namespace StardewModdingAPI.Framework
             LocalizedContentManager.LanguageCode languageCode = this.ContentCore.Language;
 
             // update mod translation helpers
-            foreach (IModMetadata mod in this.ModRegistry.GetAll(contentPacks: false))
-                (mod.Mod.Helper.Translation as TranslationHelper)?.SetLocale(locale, languageCode);
+            foreach (IModMetadata mod in this.ModRegistry.GetAll())
+                mod.Translations.SetLocale(locale, languageCode);
         }
 
         /// <summary>Run a loop handling console input.</summary>
@@ -725,8 +725,9 @@ namespace StardewModdingAPI.Framework
                         LogSkip(contentPack, errorPhrase, errorDetails);
                 }
             }
-            IModMetadata[] loadedContentPacks = this.ModRegistry.GetAll(assemblyMods: false).ToArray();
-            IModMetadata[] loadedMods = this.ModRegistry.GetAll(contentPacks: false).ToArray();
+            IModMetadata[] loaded = this.ModRegistry.GetAll().ToArray();
+            IModMetadata[] loadedContentPacks = loaded.Where(p => p.IsContentPack).ToArray();
+            IModMetadata[] loadedMods = loaded.Where(p => !p.IsContentPack).ToArray();
 
             // unlock content packs
             this.ModRegistry.AreAllModsLoaded = true;
@@ -766,10 +767,10 @@ namespace StardewModdingAPI.Framework
             }
 
             // log mod warnings
-            this.LogModWarnings(this.ModRegistry.GetAll().ToArray(), skippedMods);
+            this.LogModWarnings(loaded, skippedMods);
 
             // initialise translations
-            this.ReloadTranslations(loadedMods);
+            this.ReloadTranslations(loaded);
 
             // initialise loaded non-content-pack mods
             foreach (IModMetadata metadata in loadedMods)
@@ -919,8 +920,9 @@ namespace StardewModdingAPI.Framework
                 IManifest manifest = mod.Manifest;
                 IMonitor monitor = this.GetSecondaryMonitor(mod.DisplayName);
                 IContentHelper contentHelper = new ContentHelper(this.ContentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, monitor);
-                IContentPack contentPack = new ContentPack(mod.DirectoryPath, manifest, contentHelper, jsonHelper);
-                mod.SetMod(contentPack, monitor);
+                TranslationHelper translationHelper = new TranslationHelper(manifest.UniqueID, manifest.Name, contentCore.GetLocale(), contentCore.Language);
+                IContentPack contentPack = new ContentPack(mod.DirectoryPath, manifest, contentHelper, translationHelper, jsonHelper);
+                mod.SetMod(contentPack, monitor, translationHelper);
                 this.ModRegistry.Add(mod);
 
                 errorReasonPhrase = null;
@@ -982,6 +984,7 @@ namespace StardewModdingAPI.Framework
 
                     // init mod helpers
                     IMonitor monitor = this.GetSecondaryMonitor(mod.DisplayName);
+                    TranslationHelper translationHelper = new TranslationHelper(manifest.UniqueID, manifest.Name, contentCore.GetLocale(), contentCore.Language);
                     IModHelper modHelper;
                     {
                         IModEvents events = new ModEvents(mod, this.EventManager);
@@ -992,13 +995,13 @@ namespace StardewModdingAPI.Framework
                         IReflectionHelper reflectionHelper = new ReflectionHelper(manifest.UniqueID, mod.DisplayName, this.Reflection);
                         IModRegistry modRegistryHelper = new ModRegistryHelper(manifest.UniqueID, this.ModRegistry, proxyFactory, monitor);
                         IMultiplayerHelper multiplayerHelper = new MultiplayerHelper(manifest.UniqueID, this.GameInstance.Multiplayer);
-                        ITranslationHelper translationHelper = new TranslationHelper(manifest.UniqueID, manifest.Name, contentCore.GetLocale(), contentCore.Language);
 
                         IContentPack CreateFakeContentPack(string packDirPath, IManifest packManifest)
                         {
                             IMonitor packMonitor = this.GetSecondaryMonitor(packManifest.Name);
                             IContentHelper packContentHelper = new ContentHelper(contentCore, packDirPath, packManifest.UniqueID, packManifest.Name, packMonitor);
-                            return new ContentPack(packDirPath, packManifest, packContentHelper, this.Toolkit.JsonHelper);
+                            ITranslationHelper packTranslationHelper = new TranslationHelper(packManifest.UniqueID, packManifest.Name, contentCore.GetLocale(), contentCore.Language);
+                            return new ContentPack(packDirPath, packManifest, packContentHelper, packTranslationHelper, this.Toolkit.JsonHelper);
                         }
 
                         modHelper = new ModHelper(manifest.UniqueID, mod.DirectoryPath, this.GameInstance.Input, events, contentHelper, contentPackHelper, commandHelper, dataHelper, modRegistryHelper, reflectionHelper, multiplayerHelper, translationHelper);
@@ -1010,7 +1013,7 @@ namespace StardewModdingAPI.Framework
                     modEntry.Monitor = monitor;
 
                     // track mod
-                    mod.SetMod(modEntry);
+                    mod.SetMod(modEntry, translationHelper);
                     this.ModRegistry.Add(mod);
                     return true;
                 }
@@ -1025,7 +1028,7 @@ namespace StardewModdingAPI.Framework
         /// <summary>Write a summary of mod warnings to the console and log.</summary>
         /// <param name="mods">The loaded mods.</param>
         /// <param name="skippedMods">The mods which were skipped, along with the friendly and developer reasons.</param>
-        private void LogModWarnings(IModMetadata[] mods, IDictionary<IModMetadata, Tuple<string, string>> skippedMods)
+        private void LogModWarnings(IEnumerable<IModMetadata> mods, IDictionary<IModMetadata, Tuple<string, string>> skippedMods)
         {
             // get mods with warnings
             IModMetadata[] modsWithWarnings = mods.Where(p => p.Warnings != ModWarning.None).ToArray();
@@ -1165,9 +1168,6 @@ namespace StardewModdingAPI.Framework
             JsonHelper jsonHelper = this.Toolkit.JsonHelper;
             foreach (IModMetadata metadata in mods)
             {
-                if (metadata.IsContentPack)
-                    throw new InvalidOperationException("Can't reload translations for a content pack.");
-
                 // read translation files
                 IDictionary<string, IDictionary<string, string>> translations = new Dictionary<string, IDictionary<string, string>>();
                 DirectoryInfo translationsDir = new DirectoryInfo(Path.Combine(metadata.DirectoryPath, "i18n"));
@@ -1217,8 +1217,7 @@ namespace StardewModdingAPI.Framework
                 }
 
                 // update translation
-                TranslationHelper translationHelper = (TranslationHelper)metadata.Mod.Helper.Translation;
-                translationHelper.SetTranslations(translations);
+                metadata.Translations.SetTranslations(translations);
             }
         }
 
-- 
cgit