summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-03-26 19:08:25 -0400
committerJesse Plamondon-Willard <Pathoschild@users.noreply.github.com>2022-03-26 19:08:25 -0400
commit03efea26676464933513383eb1c841f1ca5db34d (patch)
tree6f9845ddca8ebae2d510affadfac025a30b321d6
parenteebd8d54dc068cff2b5127a4b8f03d0b54b89542 (diff)
downloadSMAPI-03efea26676464933513383eb1c841f1ca5db34d.tar.gz
SMAPI-03efea26676464933513383eb1c841f1ca5db34d.tar.bz2
SMAPI-03efea26676464933513383eb1c841f1ca5db34d.zip
add LocaleChanged content event (#766)
-rw-r--r--src/SMAPI/Events/IContentEvents.cs4
-rw-r--r--src/SMAPI/Events/LocaleChangedEventArgs.cs45
-rw-r--r--src/SMAPI/Framework/Events/EventManager.cs4
-rw-r--r--src/SMAPI/Framework/Events/ModContentEvents.cs7
-rw-r--r--src/SMAPI/Framework/SCore.cs62
5 files changed, 100 insertions, 22 deletions
diff --git a/src/SMAPI/Events/IContentEvents.cs b/src/SMAPI/Events/IContentEvents.cs
index abbaaf33..d537db70 100644
--- a/src/SMAPI/Events/IContentEvents.cs
+++ b/src/SMAPI/Events/IContentEvents.cs
@@ -19,5 +19,9 @@ namespace StardewModdingAPI.Events
/// <summary>Raised after an asset is loaded by the content pipeline, after all mod edits specified via <see cref="AssetRequested"/> have been applied.</summary>
/// <remarks>This event is only raised if something requested the asset from the content pipeline. Invalidating an asset from the content cache won't necessarily reload it automatically.</remarks>
event EventHandler<AssetReadyEventArgs> AssetReady;
+
+ /// <summary>Raised after the game language changes.</summary>
+ /// <remarks>For non-English players, this may be raised during startup when the game switches to the previously selected language.</remarks>
+ event EventHandler<LocaleChangedEventArgs> LocaleChanged;
}
}
diff --git a/src/SMAPI/Events/LocaleChangedEventArgs.cs b/src/SMAPI/Events/LocaleChangedEventArgs.cs
new file mode 100644
index 00000000..09d3f6e5
--- /dev/null
+++ b/src/SMAPI/Events/LocaleChangedEventArgs.cs
@@ -0,0 +1,45 @@
+using System;
+using LanguageCode = StardewValley.LocalizedContentManager.LanguageCode;
+
+namespace StardewModdingAPI.Events
+{
+ /// <summary>Event arguments for an <see cref="IContentEvents.LocaleChanged"/> event.</summary>
+ public class LocaleChangedEventArgs : EventArgs
+ {
+ /*********
+ ** Accessors
+ *********/
+ /// <summary>The previous language enum value.</summary>
+ /// <remarks>For a custom language, this is always <see cref="LanguageCode.mod"/>.</remarks>
+ public LanguageCode OldLanguage { get; }
+
+ /// <summary>The previous locale code.</summary>
+ /// <remarks>This is the locale code as it appears in asset names, like <c>fr-FR</c> in <c>Maps/springobjects.fr-FR</c>. The locale code for English is an empty string.</remarks>
+ public string OldLocale { get; }
+
+ /// <summary>The new language enum value.</summary>
+ /// <remarks><inheritdoc cref="OldLanguage" select="remarks" /></remarks>
+ public LanguageCode NewLanguage { get; }
+
+ /// <summary>The new locale code.</summary>
+ /// <remarks><inheritdoc cref="OldLocale" select="remarks" /></remarks>
+ public string NewLocale { get; }
+
+
+ /*********
+ ** Public methods
+ *********/
+ /// <summary>Construct an instance.</summary>
+ /// <param name="oldLanguage">The previous language enum value.</param>
+ /// <param name="oldLocale">The previous locale code.</param>
+ /// <param name="newLanguage">The new language enum value.</param>
+ /// <param name="newLocale">The new locale code.</param>
+ internal LocaleChangedEventArgs(LanguageCode oldLanguage, string oldLocale, LanguageCode newLanguage, string newLocale)
+ {
+ this.OldLanguage = oldLanguage;
+ this.OldLocale = oldLocale;
+ this.NewLanguage = newLanguage;
+ this.NewLocale = newLocale;
+ }
+ }
+}
diff --git a/src/SMAPI/Framework/Events/EventManager.cs b/src/SMAPI/Framework/Events/EventManager.cs
index bcfd7dd7..41540047 100644
--- a/src/SMAPI/Framework/Events/EventManager.cs
+++ b/src/SMAPI/Framework/Events/EventManager.cs
@@ -20,6 +20,9 @@ namespace StardewModdingAPI.Framework.Events
/// <inheritdoc cref="IContentEvents.AssetReady" />
public readonly ManagedEvent<AssetReadyEventArgs> AssetReady;
+ /// <inheritdoc cref="IContentEvents.LocaleChanged" />
+ public readonly ManagedEvent<LocaleChangedEventArgs> LocaleChanged;
+
/****
** Display
@@ -204,6 +207,7 @@ namespace StardewModdingAPI.Framework.Events
this.AssetRequested = ManageEventOf<AssetRequestedEventArgs>(nameof(IModEvents.Content), nameof(IContentEvents.AssetRequested));
this.AssetsInvalidated = ManageEventOf<AssetsInvalidatedEventArgs>(nameof(IModEvents.Content), nameof(IContentEvents.AssetsInvalidated));
this.AssetReady = ManageEventOf<AssetReadyEventArgs>(nameof(IModEvents.Content), nameof(IContentEvents.AssetReady));
+ this.LocaleChanged = ManageEventOf<LocaleChangedEventArgs>(nameof(IModEvents.Content), nameof(IContentEvents.LocaleChanged));
this.MenuChanged = ManageEventOf<MenuChangedEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.MenuChanged));
this.Rendering = ManageEventOf<RenderingEventArgs>(nameof(IModEvents.Display), nameof(IDisplayEvents.Rendering), isPerformanceCritical: true);
diff --git a/src/SMAPI/Framework/Events/ModContentEvents.cs b/src/SMAPI/Framework/Events/ModContentEvents.cs
index cb242e99..beb96031 100644
--- a/src/SMAPI/Framework/Events/ModContentEvents.cs
+++ b/src/SMAPI/Framework/Events/ModContentEvents.cs
@@ -30,6 +30,13 @@ namespace StardewModdingAPI.Framework.Events
remove => this.EventManager.AssetReady.Remove(value);
}
+ /// <inheritdoc />
+ public event EventHandler<LocaleChangedEventArgs> LocaleChanged
+ {
+ add => this.EventManager.LocaleChanged.Add(value, this.Mod);
+ remove => this.EventManager.LocaleChanged.Remove(value);
+ }
+
/*********
** Public methods
diff --git a/src/SMAPI/Framework/SCore.cs b/src/SMAPI/Framework/SCore.cs
index eab977ac..5deb177c 100644
--- a/src/SMAPI/Framework/SCore.cs
+++ b/src/SMAPI/Framework/SCore.cs
@@ -46,6 +46,7 @@ using StardewModdingAPI.Utilities;
using StardewValley;
using StardewValley.Menus;
using xTile.Display;
+using LanguageCode = StardewValley.LocalizedContentManager.LanguageCode;
using MiniMonoModHotfix = MonoMod.Utils.MiniMonoModHotfix;
using PathUtilities = StardewModdingAPI.Toolkit.Utilities.PathUtilities;
using SObject = StardewValley.Object;
@@ -62,7 +63,7 @@ namespace StardewModdingAPI.Framework
** Low-level components
****/
/// <summary>Tracks whether the game should exit immediately and any pending initialization should be cancelled.</summary>
- private readonly CancellationTokenSource CancellationToken = new CancellationTokenSource();
+ private readonly CancellationTokenSource CancellationToken = new();
/// <summary>Manages the SMAPI console window and log file.</summary>
private readonly LogManager LogManager;
@@ -71,16 +72,16 @@ namespace StardewModdingAPI.Framework
private Monitor Monitor => this.LogManager.Monitor;
/// <summary>Simplifies access to private game code.</summary>
- private readonly Reflector Reflection = new Reflector();
+ private readonly Reflector Reflection = new();
/// <summary>Encapsulates access to SMAPI core translations.</summary>
- private readonly Translator Translator = new Translator();
+ private readonly Translator Translator = new();
/// <summary>The SMAPI configuration settings.</summary>
private readonly SConfig Settings;
/// <summary>The mod toolkit used for generic mod interactions.</summary>
- private readonly ModToolkit Toolkit = new ModToolkit();
+ private readonly ModToolkit Toolkit = new();
/****
** Higher-level components
@@ -99,7 +100,7 @@ namespace StardewModdingAPI.Framework
/// <summary>Tracks the installed mods.</summary>
/// <remarks>This is initialized after the game starts.</remarks>
- private readonly ModRegistry ModRegistry = new ModRegistry();
+ private readonly ModRegistry ModRegistry = new();
/// <summary>Manages SMAPI events for mods.</summary>
private readonly EventManager EventManager;
@@ -129,18 +130,21 @@ namespace StardewModdingAPI.Framework
/// <summary>Whether the player just returned to the title screen.</summary>
public bool JustReturnedToTitle { get; set; }
+ /// <summary>The last language set by the game.</summary>
+ private (string Locale, LanguageCode Code) LastLanguage { get; set; } = ("", LanguageCode.en);
+
/// <summary>The maximum number of consecutive attempts SMAPI should make to recover from an update error.</summary>
- private readonly Countdown UpdateCrashTimer = new Countdown(60); // 60 ticks = roughly one second
+ private readonly Countdown UpdateCrashTimer = new(60); // 60 ticks = roughly one second
/// <summary>Asset interceptors added or removed since the last tick.</summary>
- private readonly List<AssetInterceptorChange> ReloadAssetInterceptorsQueue = new List<AssetInterceptorChange>();
+ private readonly List<AssetInterceptorChange> ReloadAssetInterceptorsQueue = new();
/// <summary>A list of queued commands to parse and execute.</summary>
/// <remarks>This property must be thread-safe, since it's accessed from a separate console input thread.</remarks>
- private readonly ConcurrentQueue<string> RawCommandQueue = new ConcurrentQueue<string>();
+ private readonly ConcurrentQueue<string> RawCommandQueue = new();
/// <summary>A list of commands to execute on each screen.</summary>
- private readonly PerScreen<List<Tuple<Command, string, string[]>>> ScreenCommandQueue = new PerScreen<List<Tuple<Command, string, string[]>>>(() => new List<Tuple<Command, string, string[]>>());
+ private readonly PerScreen<List<Tuple<Command, string, string[]>>> ScreenCommandQueue = new(() => new List<Tuple<Command, string, string[]>>());
/*********
@@ -369,13 +373,13 @@ namespace StardewModdingAPI.Framework
xTile.Format.FormatManager.Instance.RegisterMapFormat(new TMXTile.TMXFormat(Game1.tileSize / Game1.pixelZoom, Game1.tileSize / Game1.pixelZoom, Game1.pixelZoom, Game1.pixelZoom));
// load mod data
- ModToolkit toolkit = new ModToolkit();
+ ModToolkit toolkit = new();
ModDatabase modDatabase = toolkit.GetModDatabase(Constants.ApiMetadataPath);
// load mods
{
this.Monitor.Log("Loading mod metadata...", LogLevel.Debug);
- ModResolver resolver = new ModResolver();
+ ModResolver resolver = new();
// log loose files
{
@@ -1048,7 +1052,7 @@ namespace StardewModdingAPI.Framework
// get locale
string locale = this.ContentCore.GetLocale();
- LocalizedContentManager.LanguageCode languageCode = this.ContentCore.Language;
+ LanguageCode languageCode = this.ContentCore.Language;
// update core translations
this.Translator.SetLocale(locale, languageCode);
@@ -1061,6 +1065,20 @@ namespace StardewModdingAPI.Framework
foreach (ContentPack contentPack in mod.GetFakeContentPacks())
contentPack.TranslationImpl.SetLocale(locale, languageCode);
}
+
+ // raise event
+ if (this.EventManager.LocaleChanged.HasListeners())
+ {
+ this.EventManager.LocaleChanged.Raise(
+ new LocaleChangedEventArgs(
+ oldLanguage: this.LastLanguage.Code,
+ oldLocale: this.LastLanguage.Locale,
+ newLanguage: languageCode,
+ newLocale: locale
+ )
+ );
+ }
+ this.LastLanguage = (locale, languageCode);
}
/// <summary>Raised when the low-level stage while loading a save changes.</summary>
@@ -1385,7 +1403,7 @@ namespace StardewModdingAPI.Framework
{
// create client
string url = this.Settings.WebApiBaseUrl;
- WebApiClient client = new WebApiClient(url, Constants.ApiVersion);
+ WebApiClient client = new(url, Constants.ApiVersion);
this.Monitor.Log("Checking for updates...");
// check SMAPI version
@@ -1569,7 +1587,7 @@ namespace StardewModdingAPI.Framework
// load mods
IList<IModMetadata> skippedMods = new List<IModMetadata>();
- using (AssemblyLoader modAssemblyLoader = new AssemblyLoader(Constants.Platform, this.Monitor, this.Settings.ParanoidWarnings, this.Settings.RewriteMods))
+ using (AssemblyLoader modAssemblyLoader = new(Constants.Platform, this.Monitor, this.Settings.ParanoidWarnings, this.Settings.RewriteMods))
{
// init
HashSet<string> suppressUpdateChecks = new HashSet<string>(this.Settings.SuppressUpdateChecks, StringComparer.OrdinalIgnoreCase);
@@ -1758,7 +1776,7 @@ namespace StardewModdingAPI.Framework
IManifest manifest = mod.Manifest;
IMonitor monitor = this.LogManager.GetMonitor(mod.DisplayName);
IContentHelper contentHelper = new ContentHelper(this.ContentCore, mod.DirectoryPath, manifest.UniqueID, mod.DisplayName, monitor);
- TranslationHelper translationHelper = new TranslationHelper(manifest.UniqueID, contentCore.GetLocale(), contentCore.Language);
+ TranslationHelper translationHelper = new(manifest.UniqueID, contentCore.GetLocale(), contentCore.Language);
IContentPack contentPack = new ContentPack(mod.DirectoryPath, manifest, contentHelper, translationHelper, jsonHelper);
mod.SetMod(contentPack, monitor, translationHelper);
this.ModRegistry.Add(mod);
@@ -1831,16 +1849,16 @@ namespace StardewModdingAPI.Framework
// init mod helpers
IMonitor monitor = this.LogManager.GetMonitor(mod.DisplayName);
- TranslationHelper translationHelper = new TranslationHelper(manifest.UniqueID, contentCore.GetLocale(), contentCore.Language);
+ TranslationHelper translationHelper = new(manifest.UniqueID, contentCore.GetLocale(), contentCore.Language);
IModHelper modHelper;
{
IContentPack CreateFakeContentPack(string packDirPath, IManifest packManifest)
{
IMonitor packMonitor = this.LogManager.GetMonitor(packManifest.Name);
IContentHelper packContentHelper = new ContentHelper(contentCore, packDirPath, packManifest.UniqueID, packManifest.Name, packMonitor);
- TranslationHelper packTranslationHelper = new TranslationHelper(packManifest.UniqueID, contentCore.GetLocale(), contentCore.Language);
+ TranslationHelper packTranslationHelper = new(packManifest.UniqueID, contentCore.GetLocale(), contentCore.Language);
- ContentPack contentPack = new ContentPack(packDirPath, packManifest, packContentHelper, packTranslationHelper, this.Toolkit.JsonHelper);
+ ContentPack contentPack = new(packDirPath, packManifest, packContentHelper, packTranslationHelper, this.Toolkit.JsonHelper);
this.ReloadTranslationsForTemporaryContentPack(mod, contentPack);
mod.FakeContentPacks.Add(new WeakReference<ContentPack>(contentPack));
return contentPack;
@@ -1982,7 +2000,7 @@ namespace StardewModdingAPI.Framework
// read translation files
var translations = new Dictionary<string, IDictionary<string, string>>();
errors = new List<string>();
- DirectoryInfo translationsDir = new DirectoryInfo(folderPath);
+ DirectoryInfo translationsDir = new(folderPath);
if (translationsDir.Exists)
{
foreach (FileInfo file in translationsDir.EnumerateFiles("*.json"))
@@ -2038,7 +2056,7 @@ namespace StardewModdingAPI.Framework
{
// default path
{
- FileInfo defaultFile = new FileInfo(Path.Combine(Constants.LogDir, $"{Constants.LogFilename}.{Constants.LogExtension}"));
+ FileInfo defaultFile = new(Path.Combine(Constants.LogDir, $"{Constants.LogFilename}.{Constants.LogExtension}"));
if (!defaultFile.Exists)
return defaultFile.FullName;
}
@@ -2046,7 +2064,7 @@ namespace StardewModdingAPI.Framework
// get first disambiguated path
for (int i = 2; i < int.MaxValue; i++)
{
- FileInfo file = new FileInfo(Path.Combine(Constants.LogDir, $"{Constants.LogFilename}.player-{i}.{Constants.LogExtension}"));
+ FileInfo file = new(Path.Combine(Constants.LogDir, $"{Constants.LogFilename}.player-{i}.{Constants.LogExtension}"));
if (!file.Exists)
return file.FullName;
}
@@ -2058,7 +2076,7 @@ namespace StardewModdingAPI.Framework
/// <summary>Delete normal (non-crash) log files created by SMAPI.</summary>
private void PurgeNormalLogs()
{
- DirectoryInfo logsDir = new DirectoryInfo(Constants.LogDir);
+ DirectoryInfo logsDir = new(Constants.LogDir);
if (!logsDir.Exists)
return;