Code/Localizations/CustomLocalization.cs
using Sandbox;
using System.Collections.Generic;

namespace ExtendedBox.Localizations;

public static class CustomLocalization // TODO: remove when facepunch resolve https://github.com/Facepunch/sbox-issues/issues/3938
{
    private const string FallbackLanguageCode = "en";
    private const string LocalizationDirectory = "Localization";

    [SkipHotload]
    private static readonly Dictionary<string, string> _tokens = [];
    public static int TokensCount => _tokens.Count;

    [ConCmd("recache_localization")]
    public static void Recache(bool log = false)
    {
        _tokens.Clear();
        LoadTokens(FallbackLanguageCode, log);
        LoadTokens(Application.LanguageCode, log);
    }

    private static void LoadTokens(string languageCode, bool log = false)
    {
        var lowerLocalizationDirectory = LocalizationDirectory.ToLower();

        var path = $"{lowerLocalizationDirectory}/{languageCode}/";
        if(!FileSystem.Mounted.DirectoryExists(lowerLocalizationDirectory))
            path = $"{LocalizationDirectory}/{languageCode}/";

        if(log)
            Log.Info($"Looking for localization files in '{path}'.");
        foreach(var file in FileSystem.Mounted.FindFile(path, "*.json", false))
        {
            if(log)
                Log.Info($"Loading tokens from '{path + file}'.");
            var currentTokens = FileSystem.Mounted.ReadJson<Dictionary<string, string>>(path + file);
            foreach(var (token, value) in currentTokens)
            {
                _tokens[token] = value;
                if(log)
                    Log.Info($"Loaded token '{token}' : '{value}'.");
            }
        }
    }

    public static bool HasToken(string token)
    {
        if(!token.StartsWith('#'))
            return false;

        return _tokens.ContainsKey(token[1..]);
    }

    public static string Localize(this string token)
    {
        if(!token.StartsWith('#'))
            return token;

        token = token[1..];

        if(_tokens.Count == 0)
            Recache();

        var result = _tokens.GetValueOrDefault(token, token);
        return result;
    }
}