using System.Globalization; namespace Content.Shared.Localizations { public sealed class ContentLocalizationManager { [Dependency] private readonly ILocalizationManager _loc = default!; // If you want to change your codebase's language, do it here. private const string Culture = "en-US"; /// /// Custom format strings used for parsing and displaying minutes:seconds timespans. /// public static readonly string[] TimeSpanMinutesFormats = new[] { @"m\:ss", @"mm\:ss", @"%m", @"mm" }; public void Initialize() { var culture = new CultureInfo(Culture); _loc.LoadCulture(culture); _loc.AddFunction(culture, "PRESSURE", FormatPressure); _loc.AddFunction(culture, "POWERWATTS", FormatPowerWatts); _loc.AddFunction(culture, "POWERJOULES", FormatPowerJoules); _loc.AddFunction(culture, "UNITS", FormatUnits); _loc.AddFunction(culture, "TOSTRING", args => FormatToString(culture, args)); _loc.AddFunction(culture, "LOC", FormatLoc); } private static ILocValue FormatLoc(LocArgs args) { var id = ((LocValueString)args.Args[0]).Value; return new LocValueString(Loc.GetString(id)); } private static ILocValue FormatToString(CultureInfo culture, LocArgs args) { var arg = args.Args[0]; var fmt = ((LocValueString) args.Args[1]).Value; var obj = arg.Value; if (obj is IFormattable formattable) return new LocValueString(formattable.ToString(fmt, culture)); return new LocValueString(obj?.ToString() ?? ""); } private static ILocValue FormatUnitsGeneric(LocArgs args, string mode) { const int maxPlaces = 5; // Matches amount in _lib.ftl var pressure = ((LocValueNumber) args.Args[0]).Value; var places = 0; while (pressure > 1000 && places < maxPlaces) { pressure /= 1000; places += 1; } return new LocValueString(Loc.GetString(mode, ("divided", pressure), ("places", places))); } private static ILocValue FormatPressure(LocArgs args) { return FormatUnitsGeneric(args, "zzzz-fmt-pressure"); } private static ILocValue FormatPowerWatts(LocArgs args) { return FormatUnitsGeneric(args, "zzzz-fmt-power-watts"); } private static ILocValue FormatPowerJoules(LocArgs args) { return FormatUnitsGeneric(args, "zzzz-fmt-power-joules"); } private static ILocValue FormatUnits(LocArgs args) { if (!Units.Types.TryGetValue(((LocValueString) args.Args[0]).Value, out var ut)) throw new ArgumentException($"Unknown unit type {((LocValueString) args.Args[0]).Value}"); var fmtstr = ((LocValueString) args.Args[1]).Value; double max = Double.NegativeInfinity; var iargs = new double[args.Args.Count - 1]; for (var i = 2; i < args.Args.Count; i++) { var n = ((LocValueNumber) args.Args[i]).Value; if (n > max) max = n; iargs[i - 2] = n; } if (!ut.TryGetUnit(max, out var mu)) throw new ArgumentException("Unit out of range for type"); var fargs = new object[iargs.Length]; for (var i = 0; i < iargs.Length; i++) fargs[i] = iargs[i] * mu.Factor; fargs[^1] = Loc.GetString($"units-{mu.Unit.ToLower()}"); // Before anyone complains about "{"+"${...}", at least it's better than MS's approach... // https://docs.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting#escaping-braces // // Note that the closing brace isn't replaced so that format specifiers can be applied. var res = String.Format( fmtstr.Replace("{UNIT", "{" + $"{fargs.Length - 1}"), fargs ); return new LocValueString(res); } } }