using System.Globalization; using SPTarkov.DI.Annotations; using SPTarkov.Server.Core.Models.Spt.Config; using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Servers; namespace SPTarkov.Server.Core.Services; [Injectable(InjectionType.Singleton)] public class LocaleService( ISptLogger _logger, DatabaseServer _databaseServer, ConfigServer _configServer ) { protected readonly LocaleConfig _localeConfig = _configServer.GetConfig(); private string _chosenServerLocale = string.Empty; private string _chosenClientLocale = string.Empty; /// /// Get the eft globals db file based on the configured locale in config/locale.json, if not found, fall back to 'en' /// /// Dictionary of locales for desired language - en/fr/cn public Dictionary GetLocaleDb(string? language = null) { var languageToUse = string.IsNullOrEmpty(language) ? GetDesiredGameLocale() : language; // if it can't get locales for language provided, default to en if ( TryGetLocaleDb(languageToUse, out var localeToReturn) || TryGetLocaleDb("en", out localeToReturn) ) { return localeToReturn; } throw new Exception($"unable to get locales from either {languageToUse} or en"); } /// /// Attempts to retrieve the locale database for the specified language key /// /// The language key for which the locale database should be retrieved. /// The resulting locale database as a dictionary, or null if the operation fails. /// True if the locale database was successfully retrieved, otherwise false. protected bool TryGetLocaleDb( string languageKey, out Dictionary? localeToReturn ) { localeToReturn = null; if ( !_databaseServer .GetTables() .Locales.Global.TryGetValue(languageKey, out var keyedLocales) ) { return false; } localeToReturn = keyedLocales.Value; return true; } /// /// Gets the game locale key from the locale.json file, /// if value is 'system' get system-configured locale /// /// Locale e.g en/ge/cz/cn public string GetDesiredGameLocale() { if (string.IsNullOrEmpty(_chosenClientLocale)) { _chosenClientLocale = string.Equals( _localeConfig.GameLocale, "system", StringComparison.OrdinalIgnoreCase ) ? GetPlatformForClientLocale() : _localeConfig.GameLocale.ToLower(); // Use custom locale value } return _chosenClientLocale; } /// /// Gets the game locale key from the locale.json file, /// if value is 'system' get system locale /// /// Locale e.g en/ge/cz/cn public string GetDesiredServerLocale() { if (string.IsNullOrEmpty(_chosenServerLocale)) { _chosenServerLocale = string.Equals( _localeConfig.ServerLocale, "system", StringComparison.OrdinalIgnoreCase ) ? GetPlatformForServerLocale() : _localeConfig.ServerLocale.ToLower(); // Use custom locale value } return _chosenServerLocale; } /// /// Get array of languages supported for localisation /// /// List of locales e.g. en/fr/cn public List GetServerSupportedLocales() { return _localeConfig.ServerSupportedLocales; } /// /// Get array of languages supported for localisation /// /// Dictionary of locales e.g. en/fr/cn public Dictionary GetLocaleFallbacks() { return _localeConfig.Fallbacks; } /// /// Get the full locale of the computer running the server lowercased e.g. en-gb / pt-pt /// /// System locale as String public string GetPlatformForServerLocale() { var platformLocale = GetPlatformLocale(); if (platformLocale == null) { _logger.Warning("System language not found, falling back to english"); return "en"; } var baseNameCode = platformLocale.TwoLetterISOLanguageName.ToLower(); if (_localeConfig.ServerSupportedLocales.Contains(baseNameCode)) { // Found a matching locale return baseNameCode; } // Check if base language (e.g. CN / EN / DE) exists var languageCode = platformLocale.Name.ToLower(); if (_localeConfig.ServerSupportedLocales.Contains(languageCode)) { if (baseNameCode == "zh") // Handle edge case of zh { return "zh-cn"; } return languageCode; } if (baseNameCode == "pt") // Handle edge case of pt { return "pt-pt"; } _logger.Debug( $"Unsupported system language found: {baseNameCode}, langCode: {languageCode} falling back to english for server locale" ); return "en"; } /// /// Get the locale of the computer running the server /// /// Language part of locale e.g. 'en' part of 'en-US' protected string GetPlatformForClientLocale() { var platformLocale = GetPlatformLocale(); if (platformLocale == null) { _logger.Warning("System language not found, falling back to english"); return "en"; } var locales = _databaseServer.GetTables().Locales; var baseNameCode = platformLocale.TwoLetterISOLanguageName.ToLower(); if (locales.Global.ContainsKey(baseNameCode)) { return baseNameCode; } var languageCode = platformLocale.Name.ToLower(); if (locales.Global.ContainsKey(languageCode)) { return languageCode; } // language code wasn't found, if it's over 2 characters // we can try taking first 2 characters and see if we have a locale that matches if (languageCode.Length > 2) { // Take first 2 characters and see if that exists if (locales.Global.ContainsKey(languageCode[..1])) { return languageCode; } } // BSG map DE to GE some reason if (baseNameCode == "de") { return "ge"; } if (baseNameCode == "zh") // Handle edge case of zh { return "cn"; } _logger.Debug( $"Unsupported system language found: {languageCode} baseLocale: {baseNameCode}, falling back to english for client locale" ); return "en"; } /// /// Get the current machines locale data /// /// The current platform locale protected static CultureInfo GetPlatformLocale() { return CultureInfo.InstalledUICulture; } public List GetLocaleKeysThatStartsWithValue(string partialKey) { return GetLocaleDb().Keys.Where(x => x.StartsWith(partialKey)).ToList(); } /// /// Blank out the "test" mail message from prapor /// protected Dictionary RemovePraporTestMessage( Dictionary dbLocales ) { dbLocales["61687e2c3e526901fa76baf9"] = ""; return dbLocales; } }