implemented ability to add custom locales

change was relatively small so added it directly to localService
anything dealing with locales should use this service and not directly with DatabaseService.GetTables().Locales.Global as injected locales will be lost due to lazyloading
This commit is contained in:
CWX
2025-04-17 13:19:55 +01:00
parent 74f755af61
commit f83b4a9138
2 changed files with 85 additions and 84 deletions
@@ -1,75 +0,0 @@
using SPTarkov.Server.Core.Models.Utils;
using SPTarkov.Common.Annotations;
namespace SPTarkov.Server.Core.Services;
[Injectable(InjectionType.Singleton)]
public class CustomLocaleService(
ISptLogger<CustomLocaleService> logger
)
{
protected Dictionary<string, Dictionary<string, string>> customServerLocales = new();
protected Dictionary<string, Dictionary<string, string>> customClientLocales = new();
/// <summary>
/// Path should link to a folder containing every locale that should be added to the server locales
/// e.g. en.json for english, fr.json for french. <br/>
/// Inside each JSON should be a Dictionary of the locale key and localised text
/// </summary>
/// <param name="locale">en/fr/de</param>
/// <param name="localeKey">locale key to store values against</param>
/// <param name="localeValue">Localised string to store</param>
public void AddServerLocales(string locale, string localeKey, string localeValue)
{
AddToDictionary(locale, localeKey, localeValue, customServerLocales);
}
/// <summary>
/// Path should link to a folder containing every locale that should be added to the game locales
/// e.g. en.json for english, fr.json for french. <br/>
/// Inside each JSON should be a Dictionary of the locale key and localised text
/// </summary>
/// <param name="locale">en/fr/de</param>
/// <param name="localeKey">locale key to store values against</param>
/// <param name="localeValue">Localised string to store</param>
public void AddGameLocales(string locale, string localeKey, string localeValue)
{
AddToDictionary(locale, localeKey, localeValue, customClientLocales);
}
protected void AddToDictionary(string locale, string localeKey, string localeValue,
Dictionary<string, Dictionary<string, string>> dictionaryToAddTo)
{
dictionaryToAddTo.TryAdd(locale, new Dictionary<string, string>());
if (!dictionaryToAddTo.TryGetValue(locale, out var localeDictToAddTo))
{
logger.Error($"Unable to get custom locale dictionary keyed by: {locale}");
return;
}
if (!localeDictToAddTo.TryAdd(localeKey, localeValue))
{
logger.Error($"Unable to add: {localeKey} {localeValue} to custom locale dictionary: {locale}");
}
}
public string? GetServerValue(string locale, string localeKey)
{
return GetValueFromDictionary(locale, localeKey, customServerLocales);
}
public string? GetClientValue(string locale, string localeKey)
{
return GetValueFromDictionary(locale, localeKey, customClientLocales);
}
protected string? GetValueFromDictionary(string locale, string localeKey,
Dictionary<string, Dictionary<string, string>> dictionaryToGetFrom)
{
return dictionaryToGetFrom.TryGetValue(locale, out var localeDictToGetFrom)
? localeDictToGetFrom.GetValueOrDefault(localeKey) // Locale exists, look up value or return null
: null; // No locale (e.g. en/fr/de) at all
}
}
@@ -3,6 +3,7 @@ using SPTarkov.Server.Core.Models.Spt.Config;
using SPTarkov.Server.Core.Models.Utils;
using SPTarkov.Server.Core.Servers;
using SPTarkov.Common.Annotations;
using SPTarkov.Server.Core.Utils.Json;
namespace SPTarkov.Server.Core.Services;
@@ -13,24 +14,68 @@ public class LocaleService(
ConfigServer _configServer
)
{
// we have to LazyLoad the data from the database and then combine it with the custom data before returning it
protected LocaleConfig _localeConfig = _configServer.GetConfig<LocaleConfig>();
protected Dictionary<string, Dictionary<string, string>> customClientLocales = new Dictionary<string, Dictionary<string, string>>();
/// <summary>
/// Get the eft globals db file based on the configured locale in config/locale.json, if not found, fall back to 'en'
/// Get the eft globals db file based on the configured locale in config/locale.json, if not found, fall back to 'en'
/// This will contain Custom locales added by mods
/// </summary>
/// <returns> Dictionary </returns>
public Dictionary<string, string> GetLocaleDb()
/// <returns> Dictionary of locales for desired language - en/fr/cn </returns>
public Dictionary<string, string> GetLocaleDb(string? language = null)
{
if (_databaseServer.GetTables().Locales.Global.TryGetValue(GetDesiredGameLocale(), out var desiredLocale))
var languageToUse = string.IsNullOrEmpty(language) ? GetDesiredGameLocale() : language;
Dictionary<string, string>? localeToReturn;
// if it can't get locales for language provided, default to en
if (TryGetLocaleDbWithCustomLocales(languageToUse, out localeToReturn) ||
TryGetLocaleDbWithCustomLocales("en", out localeToReturn))
{
return desiredLocale.Value;
// TODO: need to see if this needs to be cloned
return RemovePraporTestMessage(localeToReturn);
}
_logger.Warning(
$"Unable to find desired locale file using locale: {GetDesiredGameLocale()} from config/locale.json, falling back to 'en'"
);
throw new Exception($"unable to get locales from either {languageToUse} or en");
}
return _databaseServer.GetTables().Locales.Global["en"].Value;
/// <summary>
/// Attempts to retrieve the locale database for the specified language key, including custom locales if available.
/// </summary>
/// <param name="languageKey">The language key for which the locale database should be retrieved.</param>
/// <param name="localeToReturn">The resulting locale database as a dictionary, or null if the operation fails.</param>
/// <returns>True if the locale database was successfully retrieved, otherwise false.</returns>
private bool TryGetLocaleDbWithCustomLocales(string languageKey, out Dictionary<string, string>? localeToReturn)
{
localeToReturn = null;
if (!_databaseServer.GetTables().Locales.Global.TryGetValue(languageKey, out var keyedLocales))
{
return false;
}
localeToReturn = keyedLocales.Value;
if (customClientLocales.TryGetValue(languageKey, out var customClientLocale))
{
// there were custom locales for this language
localeToReturn = CombineDbWithCustomLocales(localeToReturn, customClientLocale);
}
return true;
}
/// <summary>
/// Combines the provided database locales with custom locales, ensuring that all entries are merged into a single dictionary.
/// Custom locale entries will overwrite existing keys from the database locales if conflicts occur.
///
/// </summary>
/// <param name="dbLocales">The dictionary containing locale entries from the database.</param>
/// <param name="customLocales">The dictionary containing custom locale entries to be merged.</param>
/// <returns>A dictionary representing the merged result of database and custom locales.</returns>
private Dictionary<string, string> CombineDbWithCustomLocales(Dictionary<string, string> dbLocales, Dictionary<string, string> customLocales)
{
return dbLocales.Union(customLocales).ToDictionary(x => x.Key, x => x.Value);
}
/// <summary>
@@ -179,4 +224,35 @@ public class LocaleService(
{
return GetLocaleDb().Keys.Where(x => x.StartsWith(partialKey)).ToList();
}
public void AddCustomClientLocale(string locale, string localeKey, string localeValue)
{
AddToDictionary(locale, localeKey, localeValue, customClientLocales);
}
private void AddToDictionary(string locale, string localeKey, string localeValue,
Dictionary<string, Dictionary<string, string>> dictionaryToAddTo)
{
dictionaryToAddTo.TryAdd(locale, new Dictionary<string, string>());
if (!dictionaryToAddTo.TryGetValue(locale, out var localeDictToAddTo))
{
_logger.Error($"Unable to get custom locale dictionary keyed by: {locale}");
return;
}
if (!localeDictToAddTo.TryAdd(localeKey, localeValue))
{
_logger.Error($"Unable to add: {localeKey} {localeValue} to custom locale dictionary: {locale}");
}
}
/// <summary>
/// Blank out the "test" mail message from prapor
/// </summary>
protected Dictionary<string, string> RemovePraporTestMessage(Dictionary<string, string> dbLocales)
{
dbLocales["61687e2c3e526901fa76baf9"] = "";
return dbLocales;
}
}