diff --git a/docs/release-notes.md b/docs/release-notes.md
index 5d113e34..c3955fc0 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -9,6 +9,7 @@ These changes have not been released yet.
* Fixed 'received message' logs shown in non-developer mode.
* For modders:
+ * Added support for content pack translations.
* Added `IContentPack.HasFile` method.
* `this.Monitor.Log` now defaults to the `Trace` log level instead of `Debug`.
* Dropped support for all deprecated APIs.
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
/// The content pack's manifest.
public IManifest Manifest { get; }
+ /// Provides translations stored in the content pack's i18n folder. See for more info.
+ public ITranslationHelper Translation { get; }
+
/*********
** Public methods
@@ -38,12 +41,14 @@ namespace StardewModdingAPI.Framework
/// The full path to the content pack's folder.
/// The content pack's manifest.
/// Provides an API for loading content assets.
+ /// Provides translations stored in the content pack's i18n folder.
/// Encapsulates SMAPI's JSON file parsing.
- 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
/// The content pack instance (if loaded and is true).
IContentPack ContentPack { get; }
+ /// The translations for this mod (if loaded).
+ TranslationHelper Translations { get; }
+
/// Writes messages to the console and log file as this mod.
IMonitor Monitor { get; }
@@ -67,12 +71,14 @@ namespace StardewModdingAPI.Framework
/// Set the mod instance.
/// The mod instance to set.
- IModMetadata SetMod(IMod mod);
+ /// The translations for this mod (if loaded).
+ IModMetadata SetMod(IMod mod, TranslationHelper translations);
/// Set the mod instance.
/// The contentPack instance to set.
/// Writes messages to the console and log file.
- IModMetadata SetMod(IContentPack contentPack, IMonitor monitor);
+ /// The translations for this mod (if loaded).
+ IModMetadata SetMod(IContentPack contentPack, IMonitor monitor, TranslationHelper translations);
/// Set the mod-provided API instance.
/// The mod-provided API.
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
/// The content pack instance (if loaded and is true).
public IContentPack ContentPack { get; private set; }
+ /// The translations for this mod (if loaded).
+ public TranslationHelper Translations { get; private set; }
+
/// Writes messages to the console and log file as this mod.
public IMonitor Monitor { get; private set; }
@@ -100,26 +104,30 @@ namespace StardewModdingAPI.Framework.ModLoading
/// Set the mod instance.
/// The mod instance to set.
- public IModMetadata SetMod(IMod mod)
+ /// The translations for this mod (if loaded).
+ 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;
}
/// Set the mod instance.
/// The contentPack instance to set.
/// Writes messages to the console and log file.
- public IModMetadata SetMod(IContentPack contentPack, IMonitor monitor)
+ /// The translations for this mod (if loaded).
+ 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 c0d5603b..937b92e1 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -426,8 +426,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);
}
/// Run a loop handling console input.
@@ -724,8 +724,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;
@@ -765,10 +766,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)
@@ -918,8 +919,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;
@@ -981,6 +983,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);
@@ -991,13 +994,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);
@@ -1009,7 +1012,7 @@ namespace StardewModdingAPI.Framework
modEntry.Monitor = monitor;
// track mod
- mod.SetMod(modEntry);
+ mod.SetMod(modEntry, translationHelper);
this.ModRegistry.Add(mod);
return true;
}
@@ -1024,7 +1027,7 @@ namespace StardewModdingAPI.Framework
/// Write a summary of mod warnings to the console and log.
/// The loaded mods.
/// The mods which were skipped, along with the friendly and developer reasons.
- private void LogModWarnings(IModMetadata[] mods, IDictionary> skippedMods)
+ private void LogModWarnings(IEnumerable mods, IDictionary> skippedMods)
{
// get mods with warnings
IModMetadata[] modsWithWarnings = mods.Where(p => p.Warnings != ModWarning.None).ToArray();
@@ -1164,9 +1167,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> translations = new Dictionary>();
DirectoryInfo translationsDir = new DirectoryInfo(Path.Combine(metadata.DirectoryPath, "i18n"));
@@ -1216,8 +1216,7 @@ namespace StardewModdingAPI.Framework
}
// update translation
- TranslationHelper translationHelper = (TranslationHelper)metadata.Mod.Helper.Translation;
- translationHelper.SetTranslations(translations);
+ metadata.Translations.SetTranslations(translations);
}
}
diff --git a/src/SMAPI/IContentPack.cs b/src/SMAPI/IContentPack.cs
index 32cbc6fc..7085c538 100644
--- a/src/SMAPI/IContentPack.cs
+++ b/src/SMAPI/IContentPack.cs
@@ -17,6 +17,9 @@ namespace StardewModdingAPI
/// The content pack's manifest.
IManifest Manifest { get; }
+ /// Provides translations stored in the content pack's i18n folder. See for more info.
+ ITranslationHelper Translation { get; }
+
/*********
** Public methods