Scripts/Utils/Localization.cs
using System;
using System.Text.Json;

namespace Milaine.Utils;

public enum LngCode {
  En,
  Es
}

public sealed class Localization {
  private const string BasePath = "Localization";
  private const string FileName = "sandbox.json";

  public LngCode CurrentLanguage { get; private set; } = LngCode.En;
  public LngCode FallbackLanguage { get; set; } = LngCode.En;

  private readonly Dictionary<string, Dictionary<string, string>> Cache = [];

  public Localization() {
    var enCode = GetCurrentLngCode(LngCode.En);
    var esCode = GetCurrentLngCode(LngCode.Es);
    var enTable = LoadLanguage(enCode);
    var esTable = LoadLanguage(esCode);

    Cache[enCode] = enTable;
    Cache[esCode] = esTable;
  }

  public string T(string token, Dictionary<string, object> data = null) {
    string phrase = GetPhrase(token);

    if (data != null) {
      foreach (var (key, val) in data) {
        phrase = phrase.Replace($"{{{key}}}", val?.ToString() ?? string.Empty);
      }
    }

    return phrase;
  }

  private string GetPhrase(string token) {
    var language = GetCurrentLngCode();
    var table = GetLanguageTable(language);

    if (table.TryGetValue(token, out var phrase)) {
      return phrase;
    }

    if (CurrentLanguage != FallbackLanguage) {
      var fallbackLanguage = GetCurrentLngCode(FallbackLanguage);

      var fallbackTable = GetLanguageTable(fallbackLanguage);
      if (fallbackTable.TryGetValue(token, out var fallbackPhrase)) {

        return fallbackPhrase;
      }
    }

    return token;
  }

  private Dictionary<string, string> GetLanguageTable(string language) {
    if (Cache.TryGetValue(language, out var cached)) {
      return cached;
    }

    var table = LoadLanguage(language);
    Cache[language] = table;

    return table;
  }

  private Dictionary<string, string> LoadLanguage(string language) {
    string path = $"{BasePath}/{language}/{FileName}";
    var result = new Dictionary<string, string>();

    if (!FileSystem.Mounted.FileExists(path)) {
      Log.Warning($"[Localization] Cant found file: {path}");
      return result;
    }

    try {
      string json = FileSystem.Mounted.ReadAllText(path);
      var parsed = JsonSerializer.Deserialize<Dictionary<string, string>>(json);

      if (parsed != null) {
        result = parsed;
      }
    }
    catch (Exception ex) {
      Log.Warning($"[Localization] Error reading '{path}': {ex.Message}");
    }

    return result;
  }

  public string GetCurrentLngCode() {
    return CurrentLanguage switch {
      LngCode.En => "en",
      LngCode.Es => "es",
      _ => "",
    };
  }

  public static string GetCurrentLngCode(LngCode lng) {
    return lng switch {
      LngCode.En => "en",
      LngCode.Es => "es",
      _ => "",
    };
  }

  public void SetLanguage(LngCode code) {
    CurrentLanguage = code;
  }

  public void SetLanguage(string raw) {
    var lng = raw switch {
      "es" => LngCode.Es,
      "en" => LngCode.En,
      _ => LngCode.En,
    };

    CurrentLanguage = lng;
  }
}