using System; using System.Collections.Generic; using System.Globalization; using JetBrains.Annotations; using Linguini.Bundle; using Robust.Shared.GameObjects; using Robust.Shared.IoC; namespace Robust.Shared.Localization { // Basically Fluent.Net is based on an ancient pre-1.0 version of fluent.js. // Said version of fluent.js was also a complete garbage fire implementation wise. // So Fluent.Net is garbage implementation wise. // ...yay // (the current implementation of fluent.js is written in TS and actually sane) // // Because of this, we can't expose it to content, so we have to wrap everything related to functions. // This basically mimics the modern typescript fluent.js API. Somewhat. /// /// Function signature runnable by localizations. /// /// Contains arguments and options passed to the function by the calling localization. public delegate ILocValue LocFunction(LocArgs args); [PublicAPI] public readonly struct LocContext { public CultureInfo Culture => Bundle.Culture; internal readonly FluentBundle Bundle; internal LocContext(FluentBundle bundle) { Bundle = bundle; } } /// /// Arguments and options passed to a localization function. /// [PublicAPI] public readonly struct LocArgs { public LocArgs(IReadOnlyList args, IReadOnlyDictionary options) { Args = args; Options = options; } /// /// Positional arguments passed to the function, in order. /// public IReadOnlyList Args { get; } /// /// Key-value options passed to the function. /// public IReadOnlyDictionary Options { get; } } /// /// A value passed around in the localization system. /// /// public interface ILocValue { /// /// Format this value to a string. /// /// /// Used when this value is interpolated directly in localizations. /// /// Context containing data like culture used. /// The formatted string. string Format(LocContext ctx); /// /// Boxed value stored by this instance. /// object? Value { get; } // Matches API doesn't work because Fluent.Net is crap. // RIP. /* /// /// Checks if this value matches a string in a select expression. /// bool Matches(LocContext bundle, string matchValue) { return false; } */ } /// /// Default implementation of a localization value. /// /// /// The idea is that inheritors could add extra data like formatting parameters /// and then use those by overriding . /// /// The type of value stored. [PublicAPI] public abstract record LocValue : ILocValue { /// /// The stored value. /// public T Value { get; init; } object? ILocValue.Value => Value; protected LocValue(T val) { Value = val; } public abstract string Format(LocContext ctx); /* public virtual bool Matches(LocContext bundle, string matchValue) { return false; } */ } public sealed record LocValueNumber(double Value) : LocValue(Value) { public override string Format(LocContext ctx) { return Value.ToString(ctx.Culture); } } public sealed record LocValueDateTime(DateTime Value) : LocValue(Value) { public override string Format(LocContext ctx) { return Value.ToString(ctx.Culture); } } public sealed record LocValueTimeSpan(TimeSpan Value) : LocValue(Value) { public override string Format(LocContext ctx) { return Value.ToString("g", ctx.Culture); } } public sealed record LocValueString(string Value) : LocValue(Value) { public override string Format(LocContext ctx) { return Value; } } /// /// Stores an "invalid" string value. Produced by e.g. unresolved variable references. /// public sealed record LocValueNone(string Value) : LocValue(Value) { public override string Format(LocContext ctx) { return Value; } } public sealed record LocValueEntity(EntityUid Value) : LocValue(Value) { public override string Format(LocContext ctx) { return IoCManager.Resolve().GetComponent(Value).EntityName; } } // Matching on these doesn't work so just passing these as string for now. /*public sealed record LocValueBool(bool Value) : LocValue(Value) { public override string Format(LocContext bundle) { return Value.ToString(bundle.Culture); } /* public override bool Matches(LocContext bundle, string matchValue) { var word = Value ? "true" : "false"; return word.Equals(matchValue, StringComparison.InvariantCultureIgnoreCase); } #1# } public sealed record LocValueEnum(Enum Value) : LocValue(Value) { public override string Format(LocContext bundle) { return Value.ToString().ToLowerInvariant(); } /*public override bool Matches(LocContext bundle, string matchValue) { return matchValue.Equals(Value.ToString(), StringComparison.InvariantCultureIgnoreCase); }#1# }*/ }