@@ -11,7 +11,8 @@ public class DataCallbacks(
|
||||
HttpResponseUtil _httpResponseUtil,
|
||||
DatabaseService _databaseService,
|
||||
TraderController _traderController,
|
||||
HideoutController _hideoutController
|
||||
HideoutController _hideoutController,
|
||||
LocaleService _localeService
|
||||
)
|
||||
{
|
||||
/// <summary>
|
||||
@@ -133,10 +134,9 @@ public class DataCallbacks(
|
||||
public string GetLocalesGlobal(string url, EmptyRequestData _, string sessionID)
|
||||
{
|
||||
var localeId = url.Replace("/client/locale/", "");
|
||||
var locales = _databaseService.GetLocales();
|
||||
var result = locales.Global?[localeId].Value ?? locales.Global?.FirstOrDefault(m => m.Key == "en").Value.Value;
|
||||
var locales = _localeService.GetLocaleDb(localeId);
|
||||
|
||||
return _httpResponseUtil.GetUnclearedBody(result);
|
||||
return _httpResponseUtil.GetUnclearedBody(locales);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
+2
-4
@@ -296,15 +296,13 @@ public class GiveSptCommand(
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Return the desired locale, falls back to english if it cannot be found
|
||||
/// Return the desired locale, falls back to english if it cannot be found
|
||||
/// </summary>
|
||||
/// <param name="desiredLocale">Locale code, e.g. "fr" for french</param>
|
||||
/// <returns></returns>
|
||||
protected Dictionary<string, string> GetGlobalsLocale(string desiredLocale)
|
||||
{
|
||||
return _databaseService.GetLocales().Global.TryGetValue(desiredLocale, out var locale)
|
||||
? locale.Value
|
||||
: _databaseService.GetLocales().Global["en"].Value;
|
||||
return _localeService.GetLocaleDb(desiredLocale);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,8 @@ namespace SPTarkov.Server.Core.Models.Spt.Server;
|
||||
public record LocaleBase
|
||||
{
|
||||
[JsonPropertyName("global")]
|
||||
/// DO NOT USE THIS PROPERTY DIRECTLY, USE LOCALESERVICE INSTEAD
|
||||
/// THIS IS LAZY LOADED AND YOUR CHANGES WILL NOT BE SAVED
|
||||
public Dictionary<string, LazyLoad<Dictionary<string, string>>>? Global
|
||||
{
|
||||
get;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ public class I18nService
|
||||
private readonly Dictionary<string, string> _fallbacks;
|
||||
private readonly FileUtil _fileUtil;
|
||||
private readonly JsonUtil _jsonUtil;
|
||||
private readonly CustomLocaleService _customLocaleService;
|
||||
private readonly LocaleService _localeService;
|
||||
|
||||
private readonly Dictionary<string, LazyLoad<Dictionary<string, string>>> _loadedLocales = new();
|
||||
private HashSet<string> _locales;
|
||||
@@ -20,11 +20,11 @@ public class I18nService
|
||||
public I18nService(
|
||||
FileUtil fileUtil,
|
||||
JsonUtil jsonUtil,
|
||||
CustomLocaleService customLocaleService,
|
||||
HashSet<string> locales,
|
||||
Dictionary<string, string> fallbacks,
|
||||
string defaultLocale,
|
||||
string directory
|
||||
string directory,
|
||||
LocaleService localeService
|
||||
)
|
||||
{
|
||||
_locales = locales;
|
||||
@@ -32,8 +32,8 @@ public class I18nService
|
||||
_defaultLocale = defaultLocale;
|
||||
_directory = directory;
|
||||
_jsonUtil = jsonUtil;
|
||||
_customLocaleService = customLocaleService;
|
||||
_fileUtil = fileUtil;
|
||||
_localeService = localeService;
|
||||
|
||||
Initialize();
|
||||
}
|
||||
@@ -91,22 +91,28 @@ public class I18nService
|
||||
|
||||
public string GetLocalisedValue(string key)
|
||||
{
|
||||
// get loaded locales for set key
|
||||
if (!_loadedLocales.TryGetValue(_setLocale, out var locales))
|
||||
{
|
||||
// if we are unable to get the "loadedLocales" for the set locale, return the key
|
||||
return key;
|
||||
}
|
||||
|
||||
// searching through loaded locales for given key
|
||||
if (!locales.Value.TryGetValue(key, out var value))
|
||||
{
|
||||
// if the key is not found in loaded locales
|
||||
// check if the key is found in the default locale
|
||||
_loadedLocales.TryGetValue(_defaultLocale, out var defaults);
|
||||
if (!defaults.Value.TryGetValue(key, out value))
|
||||
{
|
||||
value = _customLocaleService.GetClientValue(_defaultLocale, key);
|
||||
value = _localeService.GetLocaleDb(_defaultLocale).FirstOrDefault(x => x.Key == key).Value;
|
||||
}
|
||||
|
||||
return value ?? key;
|
||||
}
|
||||
|
||||
// if the key is found in the server locale, return the value
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ public class LocalisationService
|
||||
public LocalisationService(
|
||||
ISptLogger<LocalisationService> logger,
|
||||
RandomUtil randomUtil,
|
||||
CustomLocaleService customLocaleService,
|
||||
DatabaseServer databaseServer,
|
||||
LocaleService localeService,
|
||||
JsonUtil jsonUtil,
|
||||
@@ -36,11 +35,11 @@ public class LocalisationService
|
||||
_i18nService = new I18nService(
|
||||
fileUtil,
|
||||
jsonUtil,
|
||||
customLocaleService,
|
||||
localeService.GetServerSupportedLocales().ToHashSet(),
|
||||
localeService.GetLocaleFallbacks(),
|
||||
"en",
|
||||
"./Assets/database/locales/server"
|
||||
"./Assets/database/locales/server",
|
||||
localeService
|
||||
);
|
||||
_i18nService.SetLocaleByKey(localeService.GetDesiredServerLocale());
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ public class CustomItemService(
|
||||
DatabaseService databaseService,
|
||||
ItemHelper itemHelper,
|
||||
ItemBaseClassService itemBaseClassService,
|
||||
ICloner cloner
|
||||
ICloner cloner,
|
||||
LocaleService localeService
|
||||
)
|
||||
{
|
||||
/// <summary>
|
||||
@@ -206,17 +207,9 @@ public class CustomItemService(
|
||||
|
||||
newLocaleDetails ??= localeDetails[localeDetails.Keys.FirstOrDefault()];
|
||||
|
||||
// Create new record in locale file
|
||||
if (!databaseService.GetLocales().Global.TryGetValue(shortNameKey.Key, out var desiredGlobal))
|
||||
{
|
||||
logger.Error($"Unable to add locale keys to {shortNameKey.Key}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
desiredGlobal.Value[$"{newItemId} Name"] = newLocaleDetails.Name;
|
||||
desiredGlobal.Value[$"{newItemId} ShortName"] = newLocaleDetails.ShortName;
|
||||
desiredGlobal.Value[$"{newItemId} Description"] = newLocaleDetails.Description;
|
||||
localeService.AddCustomClientLocale(shortNameKey.Key, $"{newItemId} Name", newLocaleDetails.Name);
|
||||
localeService.AddCustomClientLocale(shortNameKey.Key, $"{newItemId} ShortName", newLocaleDetails.ShortName);
|
||||
localeService.AddCustomClientLocale(shortNameKey.Key, $"{newItemId} Description", newLocaleDetails.Description);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,8 +101,6 @@ public class PostDbLoadService(
|
||||
|
||||
RemoveNewBeginningRequirementFromPrestige();
|
||||
|
||||
RemovePraporTestMessage();
|
||||
|
||||
ValidateQuestAssortUnlocksExist();
|
||||
|
||||
if (_seasonalEventService.IsAutomaticEventDetectionEnabled())
|
||||
@@ -145,7 +143,7 @@ public class PostDbLoadService(
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merge custom achievements into achievement db table
|
||||
/// Merge custom achievements into achievement db table
|
||||
/// </summary>
|
||||
protected void MergeCustomAchievements()
|
||||
{
|
||||
@@ -519,19 +517,6 @@ public class PostDbLoadService(
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Blank out the "test" mail message from prapor
|
||||
/// </summary>
|
||||
protected void RemovePraporTestMessage()
|
||||
{
|
||||
// Iterate over all languages (e.g. "en", "fr")
|
||||
var locales = _databaseService.GetLocales();
|
||||
foreach (var localeKvP in locales.Global)
|
||||
{
|
||||
locales.Global[localeKvP.Key].Value["61687e2c3e526901fa76baf9"] = "";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for any missing assorts inside each traders assort.json data, checking against traders questassort.json
|
||||
/// </summary>
|
||||
|
||||
@@ -21,7 +21,8 @@ public class SeasonalEventService(
|
||||
BotHelper _botHelper,
|
||||
ProfileHelper _profileHelper,
|
||||
//DatabaseImporter _databaseImporter,
|
||||
ConfigServer _configServer
|
||||
ConfigServer _configServer,
|
||||
LocaleService _localeService
|
||||
)
|
||||
{
|
||||
private bool _christmasEventActive;
|
||||
@@ -1060,9 +1061,8 @@ public class SeasonalEventService(
|
||||
|
||||
protected void RenameBitcoin()
|
||||
{
|
||||
var enLocale = _databaseService.GetLocales().Global["en"];
|
||||
enLocale.Value[$"{ItemTpl.BARTER_PHYSICAL_BITCOIN} Name"] = "Physical SPT Coin";
|
||||
enLocale.Value[$"{ItemTpl.BARTER_PHYSICAL_BITCOIN} ShortName"] = "0.2SPT";
|
||||
_localeService.AddCustomClientLocale("en", $"{ItemTpl.BARTER_PHYSICAL_BITCOIN} Name", "Physical SPT Coin");
|
||||
_localeService.AddCustomClientLocale("en", $"{ItemTpl.BARTER_PHYSICAL_BITCOIN} ShortName", "0.2SPT");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -6,9 +6,7 @@ using SPTarkov.Server.Core.Models.Spt.Mod;
|
||||
using SPTarkov.Server.Core.Models.Utils;
|
||||
using SPTarkov.Server.Core.Utils;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Serilog.Exceptions;
|
||||
using Serilog.Extensions.Logging;
|
||||
using SPTarkov.Common.Semver;
|
||||
using SPTarkov.Common.Semver.Implementations;
|
||||
using SPTarkov.Server.Logger;
|
||||
@@ -32,7 +30,7 @@ public static class Program
|
||||
|
||||
// validate and sort mods, this will also discard any mods that are invalid
|
||||
var sortedLoadedMods = ValidateMods(mods);
|
||||
// for harmony we use the original list, as some mods may only be bepinex patches only
|
||||
// for harmony, we use the original list, as some mods may only be bepinex patches only
|
||||
HarmonyBootstrapper.LoadAllPatches(mods.SelectMany(asm => asm.Assemblies).ToList());
|
||||
|
||||
// register SPT components
|
||||
@@ -49,7 +47,7 @@ public static class Program
|
||||
try
|
||||
{
|
||||
var watermark = serviceProvider.GetService<Watermark>();
|
||||
// Initialize Watermak
|
||||
// Initialize Watermark
|
||||
watermark?.Initialize();
|
||||
|
||||
var appContext = serviceProvider.GetService<ApplicationContext>();
|
||||
@@ -72,7 +70,7 @@ public static class Program
|
||||
var app = serviceProvider.GetService<App>();
|
||||
app?.Run().Wait();
|
||||
|
||||
// Run garbage collection now server is ready to start
|
||||
// Run garbage collection now the server is ready to start
|
||||
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
|
||||
GC.Collect(GC.MaxGeneration, GCCollectionMode.Aggressive, true, true);
|
||||
|
||||
@@ -89,7 +87,6 @@ public static class Program
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
logger.LogCritical(ex, "Critical exception, stopping server...");
|
||||
// throw ex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user