Add linguini loc (#1760)

Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>
This commit is contained in:
Ygg01
2021-06-09 19:44:18 +02:00
committed by GitHub
parent 60b506cb2a
commit a8ee7be844
12 changed files with 358 additions and 281 deletions

3
.gitmodules vendored
View File

@@ -13,3 +13,6 @@
[submodule "ManagedHttpListener"]
path = ManagedHttpListener
url = https://github.com/space-wizards/ManagedHttpListener.git
[submodule "Linguini"]
path = Linguini
url = https://github.com/space-wizards/Linguini

1
Linguini Submodule

Submodule Linguini added at f161cf907a

View File

@@ -52,7 +52,7 @@ namespace Robust.Shared.Localization
/// Does not log a warning if the message does not exist.
/// Does however log errors if any occur while formatting.
/// </remarks>
bool TryGetString(string messageId, [NotNullWhen(true)] out string? value, params (string, object)[] args);
bool TryGetString(string messageId, [NotNullWhen(true)] out string? value, params (string, object)[] keyArgs);
/// <summary>
/// Default culture used by other methods when no culture is explicitly specified.

View File

@@ -25,7 +25,7 @@ namespace Robust.Shared.Localization
private static ILocalizationManager LocalizationManager => IoCManager.Resolve<ILocalizationManager>();
/// <summary>
/// Gets a language approrpiate string represented by the supplied messageId.
/// Gets a language appropriate string represented by the supplied messageId.
/// </summary>
/// <param name="messageId">Unique Identifier for a translated message.</param>
/// <returns>

View File

@@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using Fluent.Net;
using JetBrains.Annotations;
using Linguini.Bundle;
using Robust.Shared.GameObjects;
namespace Robust.Shared.Localization
@@ -25,13 +25,13 @@ namespace Robust.Shared.Localization
[PublicAPI]
public readonly struct LocContext
{
public CultureInfo Culture => Context.Culture;
public CultureInfo Culture => Bundle.Culture;
internal readonly MessageContext Context;
internal readonly FluentBundle Bundle;
internal LocContext(MessageContext ctx)
internal LocContext(FluentBundle bundle)
{
Context = ctx;
Bundle = bundle;
}
}
@@ -86,7 +86,7 @@ namespace Robust.Shared.Localization
/// <summary>
/// Checks if this value matches a string in a select expression.
/// </summary>
bool Matches(LocContext ctx, string matchValue)
bool Matches(LocContext bundle, string matchValue)
{
return false;
}
@@ -119,7 +119,7 @@ namespace Robust.Shared.Localization
public abstract string Format(LocContext ctx);
/*
public virtual bool Matches(LocContext ctx, string matchValue)
public virtual bool Matches(LocContext bundle, string matchValue)
{
return false;
}
@@ -150,6 +150,7 @@ namespace Robust.Shared.Localization
}
}
/// <summary>
/// Stores an "invalid" string value. Produced by e.g. unresolved variable references.
/// </summary>
@@ -173,13 +174,13 @@ namespace Robust.Shared.Localization
/*public sealed record LocValueBool(bool Value) : LocValue<bool>(Value)
{
public override string Format(LocContext ctx)
public override string Format(LocContext bundle)
{
return Value.ToString(ctx.Culture);
return Value.ToString(bundle.Culture);
}
/*
public override bool Matches(LocContext ctx, string matchValue)
public override bool Matches(LocContext bundle, string matchValue)
{
var word = Value ? "true" : "false";
return word.Equals(matchValue, StringComparison.InvariantCultureIgnoreCase);
@@ -189,14 +190,14 @@ namespace Robust.Shared.Localization
public sealed record LocValueEnum(Enum Value) : LocValue<Enum>(Value)
{
public override string Format(LocContext ctx)
public override string Format(LocContext bundle)
{
return Value.ToString().ToLowerInvariant();
}
/*public override bool Matches(LocContext ctx, string matchValue)
/*public override bool Matches(LocContext bundle, string matchValue)
{
return matchValue.Equals(Value.ToString(), StringComparison.InvariantCultureIgnoreCase);
}#1#
}*/
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Text;
using Linguini.Bundle.Errors;
using Linguini.Syntax.Parser.Error;
namespace Robust.Shared.Localization
{
internal static class LocHelper
{
public static string FormatCompileErrors(this ParseError self, ReadOnlyMemory<char> resource)
{
ErrorSpan span = new(self.Row, self.Slice!.Value.Start.Value, self.Slice.Value.End.Value,
self.Position.Start.Value, self.Position.End.Value);
return FormatErrors(self.Message, span, resource);
}
private static string FormatErrors(string message, ErrorSpan span, ReadOnlyMemory<char> resource)
{
var sb = new StringBuilder();
var row = $" {span.Row} ";
var errContext = resource.Slice(span.StartSpan, span.EndSpan - span.StartSpan).ToString();
sb.Append(row).Append('|')
.AppendLine(errContext);
sb.Append(' ', row.Length).Append('|')
.Append(' ', span.StartMark - span.StartSpan - 1).Append('^', span.EndMark - span.StartMark)
.AppendLine($" {message}");
return sb.ToString();
}
}
}

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Linguini.Bundle.Errors;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Localization;
using Robust.Shared.Prototypes;
@@ -41,7 +42,7 @@ namespace Robust.Shared.Localization
// Flush caches conservatively on prototype/localization changes.
private void OnPrototypesReloaded(PrototypesReloadedEventArgs args)
{
if (!args.ByType.TryGetValue(typeof(EntityPrototype), out var changeSet))
if (!args.ByType.ContainsKey(typeof(EntityPrototype)))
return;
FlushEntityCache();
@@ -52,48 +53,58 @@ namespace Robust.Shared.Localization
string? name = null;
string? desc = null;
string? suffix = null;
Dictionary<string, string>? attribs = null;
Dictionary<string, string>? attributes = null;
while (true)
{
var prototype = _prototype.Index<EntityPrototype>(prototypeId);
var locId = prototype.CustomLocalizationID ?? $"ent-{prototypeId}";
if (TryGetMessage(locId, out var ctx, out var msg))
if (TryGetMessage(locId, out var bundle, out var msg))
{
// Localization override exists.
var mAttribs = msg.Attributes;
var msgAttrs = msg.Attributes;
if (name == null && msg.Value != null)
{
// Only set name if the value isn't empty.
// So that you can override *only* a desc/suffix.
name = ctx.Format(msg.Value);
name = bundle.FormatPattern(msg.Value, null, out var fmtErr);
WriteWarningForErrs(fmtErr, locId);
}
if (mAttribs != null && mAttribs.Count != 0)
if (msgAttrs.Count != 0)
{
if (desc == null && mAttribs.TryGetValue("desc", out var mDesc))
foreach (var (attrId, pattern) in msg.Attributes)
{
desc = ctx.Format(mDesc);
}
if (suffix == null && mAttribs.TryGetValue("suffix", out var mSuffix))
{
suffix = ctx.Format(mSuffix);
}
foreach (var (attrib, value) in msg.Attributes)
{
if (attrib is "desc" or "suffix")
var attrib = attrId.ToString();
if (attrib.Equals("desc")
|| attrib.Equals("suffix"))
continue;
attribs ??= new Dictionary<string, string>();
if (!attribs.ContainsKey(attrib))
attributes ??= new Dictionary<string, string>();
if (!attributes.ContainsKey(attrib))
{
attribs[attrib] = ctx.Format(value);
var value = bundle.FormatPattern(pattern, null, out var errors);
WriteWarningForErrs(errors, locId);
attributes[attrib] = value;
}
}
var allErrors = new List<FluentError>();
if (desc == null
&& bundle.TryGetMsg(locId, "desc", null, out var err1, out desc))
{
allErrors.AddRange(err1);
}
if (suffix == null
&& bundle.TryGetMsg(locId, "suffix", null, out var err, out suffix))
{
allErrors.AddRange(err);
}
WriteWarningForErrs(allErrors, locId);
}
}
@@ -105,10 +116,10 @@ namespace Robust.Shared.Localization
{
foreach (var (attrib, value) in prototype.LocProperties)
{
attribs ??= new Dictionary<string, string>();
if (!attribs.ContainsKey(attrib))
attributes ??= new Dictionary<string, string>();
if (!attributes.ContainsKey(attrib))
{
attribs[attrib] = value;
attributes[attrib] = value;
}
}
}
@@ -123,9 +134,10 @@ namespace Robust.Shared.Localization
name ?? "",
desc ?? "",
suffix,
attribs?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty);
attributes?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty);
}
public EntityLocData GetEntityData(string prototypeId)
{
return _entityCache.GetOrAdd(prototypeId, (id, t) => t.CalcEntityLoc(id), this);

View File

@@ -1,38 +1,39 @@
using System;
#nullable enable
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Fluent.Net;
using Linguini.Bundle;
using Linguini.Bundle.Types;
using Linguini.Shared.Types.Bundle;
using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components.Localization;
using Robust.Shared.Utility;
namespace Robust.Shared.Localization
{
internal sealed partial class LocalizationManager
{
private void AddBuiltinFunctions(MessageContext context)
private void AddBuiltInFunctions(FluentBundle bundle)
{
// Grammatical gender / pronouns
AddCtxFunction(context, "GENDER", FuncGender);
AddCtxFunction(context, "SUBJECT", FuncSubject);
AddCtxFunction(context, "OBJECT", FuncObject);
AddCtxFunction(context, "POSS-ADJ", FuncPossAdj);
AddCtxFunction(context, "POSS-PRONOUN", FuncPossPronoun);
AddCtxFunction(context, "REFLEXIVE", FuncReflexive);
AddCtxFunction(bundle, "GENDER", FuncGender);
AddCtxFunction(bundle, "SUBJECT", FuncSubject);
AddCtxFunction(bundle, "OBJECT", FuncObject);
AddCtxFunction(bundle, "POSS-ADJ", FuncPossAdj);
AddCtxFunction(bundle, "POSS-PRONOUN", FuncPossPronoun);
AddCtxFunction(bundle, "REFLEXIVE", FuncReflexive);
// Conjugation
AddCtxFunction(context, "CONJUGATE-BE", FuncConjugateBe);
AddCtxFunction(context, "CONJUGATE-HAVE", FuncConjugateHave);
AddCtxFunction(bundle, "CONJUGATE-BE", FuncConjugateBe);
AddCtxFunction(bundle, "CONJUGATE-HAVE", FuncConjugateHave);
// Proper nouns
AddCtxFunction(context, "PROPER", FuncProper);
AddCtxFunction(context, "THE", FuncThe);
AddCtxFunction(bundle, "PROPER", FuncProper);
AddCtxFunction(bundle, "THE", FuncThe);
// Misc
AddCtxFunction(context, "ATTRIB", args => FuncAttrib(context, args));
AddCtxFunction(context, "CAPITALIZE", FuncCapitalize);
AddCtxFunction(bundle, "ATTRIB", args => FuncAttrib(bundle, args));
AddCtxFunction(bundle, "CAPITALIZE", FuncCapitalize);
}
/// <summary>
@@ -50,7 +51,7 @@ namespace Robust.Shared.Localization
{
var input = args.Args[0].Format(new LocContext());
if (!String.IsNullOrEmpty(input))
return new LocValueString(input.First().ToString().ToUpper() + input.Substring(1));
return new LocValueString(input[0].ToString().ToUpper() + input.Substring(1));
else return new LocValueString("");
}
@@ -64,7 +65,7 @@ namespace Robust.Shared.Localization
ILocValue entity0 = args.Args[0];
if (entity0.Value != null)
{
IEntity entity = (IEntity) entity0.Value;
IEntity entity = (IEntity)entity0.Value;
if (entity.TryGetComponent<GrammarComponent>(out var grammar) && grammar.Gender.HasValue)
{
@@ -136,16 +137,16 @@ namespace Robust.Shared.Localization
return new LocValueString(GetString("zzzz-conjugate-have", ("ent", args.Args[0])));
}
private ILocValue FuncAttrib(MessageContext context, LocArgs args)
private ILocValue FuncAttrib(FluentBundle bundle, LocArgs args)
{
if (args.Args.Count < 2) return new LocValueString("other");
ILocValue entity0 = args.Args[0];
if (entity0.Value != null)
{
IEntity entity = (IEntity) entity0.Value;
IEntity entity = (IEntity)entity0.Value;
ILocValue attrib0 = args.Args[1];
if (TryGetEntityLocAttrib(entity, attrib0.Format(new LocContext(context)), out var attrib))
if (TryGetEntityLocAttrib(entity, attrib0.Format(new LocContext(bundle)), out var attrib))
{
return new LocValueString(attrib);
}
@@ -164,7 +165,7 @@ namespace Robust.Shared.Localization
ILocValue entity0 = args.Args[0];
if (entity0.Value != null)
{
IEntity entity = (IEntity) entity0.Value;
IEntity entity = (IEntity)entity0.Value;
if (entity.TryGetComponent<GrammarComponent>(out var grammar) && grammar.ProperNoun.HasValue)
{
@@ -180,39 +181,99 @@ namespace Robust.Shared.Localization
return new LocValueString("false");
}
private void AddCtxFunction(MessageContext ctx, string name, LocFunction function)
private void AddCtxFunction(FluentBundle ctx, string name, LocFunction function)
{
ctx.Functions.Add(name, (args, options) => CallFunction(function, args, options));
ctx.AddFunction(name, (args, options)
=> CallFunction(function, args, options), out _, InsertBehavior.Overriding);
}
private IFluentType CallFunction(LocFunction function,
IList<IFluentType> positionalArgs, IDictionary<string, IFluentType> namedArgs)
{
var args = new ILocValue[positionalArgs.Count];
for (var i = 0; i < args.Length; i++)
{
args[i] = positionalArgs[i].ToLocValue();
}
var options = new Dictionary<string, ILocValue>(namedArgs.Count);
foreach (var (k, v) in namedArgs)
{
options.Add(k, v.ToLocValue());
}
var argStruct = new LocArgs(args, options);
return function.Invoke(argStruct).FluentFromVal();
}
public void AddFunction(CultureInfo culture, string name, LocFunction function)
{
var context = _contexts[culture];
var bundle = _contexts[culture];
context.Functions.Add(name, (args, options) => CallFunction(function, args, options));
}
private FluentType CallFunction(
LocFunction function,
IList<object> fluentArgs, IDictionary<string, object> fluentOptions)
{
var args = new ILocValue[fluentArgs.Count];
for (var i = 0; i < args.Length; i++)
{
args[i] = ValFromFluent(fluentArgs[i]);
}
var options = new Dictionary<string, ILocValue>(fluentOptions.Count);
foreach (var (k, v) in fluentOptions)
{
options.Add(k, ValFromFluent(v));
}
var argStruct = new LocArgs(args, options);
var ret = function(argStruct);
return ValToFluent(ret);
bundle.AddFunction(name, (args, options)
=> CallFunction(function, args, options), out _, InsertBehavior.Overriding);
}
}
}
internal sealed class FluentLocWrapperType : IFluentType
{
public readonly ILocValue WrappedValue;
public FluentLocWrapperType(ILocValue wrappedValue)
{
WrappedValue = wrappedValue;
}
public string AsString()
{
return WrappedValue.Format(new LocContext());
}
public IFluentType Copy()
{
return this;
}
}
static class LinguiniAdapter
{
internal static ILocValue ToLocValue(this IFluentType arg)
{
return arg switch
{
FluentNone => new LocValueNone(""),
FluentNumber number => new LocValueNumber(number),
FluentString str => new LocValueString(str),
FluentLocWrapperType value => value.WrappedValue,
_ => throw new ArgumentOutOfRangeException(nameof(arg)),
};
}
public static IFluentType FluentFromObject(this object obj)
{
return obj switch
{
ILocValue wrap => new FluentLocWrapperType(wrap),
IEntity entity => new FluentLocWrapperType(new LocValueEntity(entity)),
DateTime dateTime => new FluentLocWrapperType(new LocValueDateTime(dateTime)),
bool or Enum => (FluentString)obj.ToString()!.ToLowerInvariant(),
string str => (FluentString)str,
double dbl => (FluentNumber)dbl,
_ => (FluentString)obj.ToString()!,
};
}
public static IFluentType FluentFromVal(this ILocValue locValue)
{
return locValue switch
{
LocValueNone => FluentNone.None,
LocValueNumber number => (FluentNumber)number.Value,
LocValueString str => (FluentString)str.Value,
LocValueDateTime dateTime => new FluentLocWrapperType(dateTime),
_ => new FluentLocWrapperType(locValue),
};
}
}
}

View File

@@ -4,9 +4,14 @@ using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using Fluent.Net;
using Fluent.Net.RuntimeAst;
using JetBrains.Annotations;
using Linguini.Bundle;
using Linguini.Bundle.Builder;
using Linguini.Bundle.Errors;
using Linguini.Shared.Types.Bundle;
using Linguini.Syntax.Ast;
using Linguini.Syntax.Parser;
using Linguini.Syntax.Parser.Error;
using Robust.Shared.ContentPack;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
@@ -24,7 +29,7 @@ namespace Robust.Shared.Localization
[Dependency] private readonly IPrototypeManager _prototype = default!;
private ISawmill _logSawmill = default!;
private readonly Dictionary<CultureInfo, MessageContext> _contexts = new();
private readonly Dictionary<CultureInfo, FluentBundle> _contexts = new();
private CultureInfo? _defaultCulture;
@@ -48,16 +53,6 @@ namespace Robust.Shared.Localization
return msg;
}
public bool TryGetString(string messageId, [NotNullWhen(true)] out string? value)
{
if (!TryGetNode(messageId, out var context, out var node))
{
value = null;
return false;
}
return DoFormat(messageId, out value, context, node);
}
public string GetString(string messageId, params (string, object)[] args0)
{
@@ -73,144 +68,38 @@ namespace Robust.Shared.Localization
return msg;
}
public bool TryGetString(string messageId, [NotNullWhen(true)] out string? value,
params (string, object)[] args0)
public bool TryGetString(string messageId, [NotNullWhen(true)] out string? value)
{
if (!TryGetNode(messageId, out var context, out var node))
return TryGetString(messageId, out value, null);
}
public bool TryGetString(string messageId, [NotNullWhen(true)] out string? value,
params (string, object)[]? keyArgs)
{
var args = new Dictionary<string, IFluentType>();
if (keyArgs != null)
{
foreach (var (k, v) in keyArgs)
{
args.Add(k, v.FluentFromObject());
}
}
if (!HasMessage(messageId, out var bundle))
{
value = null;
return false;
}
var args = new Dictionary<string, object>();
foreach (var (k, v) in args0)
{
var val = v switch
{
IEntity entity => new LocValueEntity(entity),
bool or Enum => v.ToString()!.ToLowerInvariant(),
_ => v,
};
if (val is ILocValue locVal)
val = new FluentLocWrapperType(locVal);
args.Add(k, val);
}
return DoFormat(messageId, out value, context, node, args);
}
private bool TryGetMessage(
string messageId,
[NotNullWhen(true)] out MessageContext? ctx,
[NotNullWhen(true)] out Message? message)
{
if (_defaultCulture == null)
{
ctx = null;
message = null;
return false;
}
ctx = _contexts[_defaultCulture];
message = ctx.GetMessage(messageId);
return message != null;
}
private bool TryGetNode(
string messageId,
[NotNullWhen(true)] out MessageContext? ctx,
[NotNullWhen(true)] out Node? node)
{
string? attribName = null;
if (messageId.Contains('.'))
{
var split = messageId.Split('.');
messageId = split[0];
attribName = split[1];
}
if (!TryGetMessage(messageId, out ctx, out var message))
{
node = null;
return false;
}
if (attribName != null)
{
if (message.Attributes == null || !message.Attributes.TryGetValue(attribName, out var attrib))
{
node = null;
return false;
}
node = attrib;
}
else
{
node = message;
}
return true;
}
public void ReloadLocalizations()
{
foreach (var (culture, context) in _contexts.ToArray())
{
// Fluent.Net doesn't allow us to remove messages so...
var newContext = new MessageContext(
culture.Name,
new MessageContextOptions
{
UseIsolating = false,
Functions = context.Functions
}
);
_contexts[culture] = newContext;
_loadData(_res, culture, newContext);
}
FlushEntityCache();
}
private static ILocValue ValFromFluent(object arg)
{
return arg switch
{
FluentNone none => new LocValueNone(none.Value),
FluentNumber number => new LocValueNumber(double.Parse(number.Value)),
FluentString str => new LocValueString(str.Value),
FluentDateTime dateTime =>
new LocValueDateTime(DateTime.Parse(dateTime.Value, null, DateTimeStyles.RoundtripKind)),
FluentLocWrapperType wrap => wrap.WrappedValue,
_ => throw new ArgumentOutOfRangeException(nameof(arg))
};
}
private static FluentType ValToFluent(ILocValue arg)
{
return arg switch
{
LocValueNone =>
throw new NotSupportedException("Cannot currently return LocValueNone from loc functions."),
LocValueNumber number => new FluentNumber(number.Value.ToString("R")),
LocValueString str => new FluentString(str.Value),
LocValueDateTime dateTime => new FluentDateTime(dateTime.Value),
_ => new FluentLocWrapperType(arg)
};
}
private bool DoFormat(string messageId, out string? value, MessageContext context, Node node, IDictionary<string, object>? args = null)
{
var errs = new List<FluentError>();
try
{
value = context.Format(node, args, errs);
var result = bundle.TryGetAttrMsg(messageId, args, out var errs, out value);
foreach (var err in errs)
{
_logSawmill.Error("{culture}/{messageId}: {error}", _defaultCulture!.Name, messageId, err);
}
return result;
}
catch (Exception e)
{
@@ -218,13 +107,52 @@ namespace Robust.Shared.Localization
value = null;
return false;
}
}
foreach (var err in errs)
private bool HasMessage(
string messageId,
[NotNullWhen(true)] out FluentBundle? bundle)
{
if (_defaultCulture == null)
{
_logSawmill.Error("{culture}/{messageId}: {error}", _defaultCulture!.Name, messageId, err);
bundle = null;
return false;
}
return true;
bundle = _contexts[_defaultCulture];
if (messageId.Contains('.'))
{
var split = messageId.Split('.');
return bundle.HasMessage(split[0]);
}
return bundle.HasMessage(messageId);
}
private bool TryGetMessage(
string messageId,
[NotNullWhen(true)] out FluentBundle? bundle,
[NotNullWhen(true)] out AstMessage? message)
{
if (_defaultCulture == null)
{
bundle = null;
message = null;
return false;
}
bundle = _contexts[_defaultCulture];
return bundle.TryGetAstMessage(messageId, out message);
}
public void ReloadLocalizations()
{
foreach (var (culture, context) in _contexts.ToArray())
{
_loadData(_res, culture, context);
}
FlushEntityCache();
}
/// <summary>
@@ -261,26 +189,18 @@ namespace Robust.Shared.Localization
public void LoadCulture(CultureInfo culture)
{
var context = new MessageContext(
culture.Name,
new MessageContextOptions
{
UseIsolating = false,
// Have to pass empty dict here or else Fluent.Net will fuck up
// and share the same dict between multiple message contexts.
// Yes, you read that right.
Functions = new Dictionary<string, Resolver.ExternalFunction>(),
}
);
AddBuiltinFunctions(context);
var bundle = LinguiniBuilder.Builder()
.CultureInfo(culture)
.SkipResources()
.SetUseIsolating(false)
.UseConcurrent()
.UncheckedBuild();
_contexts.Add(culture, context);
_contexts.Add(culture, bundle);
AddBuiltInFunctions(bundle);
_loadData(_res, culture, context);
if (DefaultCulture == null)
{
DefaultCulture = culture;
}
_loadData(_res, culture, bundle);
DefaultCulture ??= culture;
}
public void AddLoadedToStringSerializer(IRobustMappedStringSerializer serializer)
@@ -307,7 +227,7 @@ namespace Robust.Shared.Localization
*/
}
private void _loadData(IResourceManager resourceManager, CultureInfo culture, MessageContext context)
private void _loadData(IResourceManager resourceManager, CultureInfo culture, FluentBundle context)
{
// Load data from .ftl files.
// Data is loaded from /Locale/<language-code>/*
@@ -323,40 +243,33 @@ namespace Robust.Shared.Localization
using var fileStream = resourceManager.ContentFileRead(path);
using var reader = new StreamReader(fileStream, EncodingHelpers.UTF8);
var resource = FluentResource.FromReader(reader);
return (path, resource);
var parser = new LinguiniParser(reader);
var resource = parser.Parse();
return (path, resource, parser.GetReadonlyData);
});
foreach (var (path, resource) in resources)
foreach (var (path, resource, data) in resources)
{
var errors = context.AddResource(resource);
foreach (var error in errors)
{
_logSawmill.Error("{path}: {exception}", path, error.Message);
}
var errors = resource.Errors;
context.AddResourceOverriding(resource);
WriteWarningForErrs(path, errors, data);
}
}
private sealed class FluentLocWrapperType : FluentType
private void WriteWarningForErrs(ResourcePath path, List<ParseError> errs, ReadOnlyMemory<char> resource)
{
public readonly ILocValue WrappedValue;
public FluentLocWrapperType(ILocValue wrappedValue)
foreach (var err in errs)
{
WrappedValue = wrappedValue;
_logSawmill.Warning("{path}:\n{exception}", path, err.FormatCompileErrors(resource));
}
}
public override string Format(MessageContext ctx)
private void WriteWarningForErrs(IList<FluentError> errs, string locId)
{
foreach (var err in errs)
{
return WrappedValue.Format(new LocContext(ctx));
}
public override bool Match(MessageContext ctx, object obj)
{
return false;
/*var strVal = obj is IFluentType ft ? ft.Value : obj.ToString() ?? "";
return WrappedValue.Matches(new LocContext(ctx), strVal);*/
_logSawmill.Warning("Error extracting `{locId}`\n{e1}", locId, err);
}
}
}
}
}

View File

@@ -14,7 +14,6 @@
<PackageReference Include="Nett" Version="0.15.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="nfluidsynth" Version="0.3.1" />
<PackageReference Include="Fluent.Net" Version="1.0.50" />
<PackageReference Include="Pidgin" Version="2.5.0" />
<PackageReference Include="prometheus-net" Version="4.1.1" />
<PackageReference Include="Robust.Shared.AuthLib" Version="0.1.2" />
@@ -24,6 +23,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Lidgren.Network\Lidgren.Network.csproj" />
<ProjectReference Include="..\Linguini\Linguini.Bundle\Linguini.Bundle.csproj" />
<ProjectReference Include="..\NetSerializer\NetSerializer\NetSerializer.csproj" />
<ProjectReference Include="..\Robust.Physics\Robust.Physics.csproj" />
<ProjectReference Include="..\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />

View File

@@ -1,4 +1,4 @@
using System.Globalization;
using System.Globalization;
using NUnit.Framework;
using Robust.Shared.ContentPack;
using Robust.Shared.GameObjects;

View File

@@ -49,6 +49,18 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.Analyzers", "Robust.
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Base", "Avalonia.Base\Avalonia.Base.csproj", "{C60905B4-072F-4376-BCEC-C91186644127}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Linguini", "Linguini", "{3D0047D0-1089-48EC-AF72-A9B7A2E7A4BF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Linguini.Bundle", "Linguini\Linguini.Bundle\Linguini.Bundle.csproj", "{71A3AD7B-9D09-4246-A574-E5EB7D7FF20B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Linguini.Shared", "Linguini\Linguini.Shared\Linguini.Shared.csproj", "{918FEA70-4B6B-4C8D-8AAB-2C911DCDCAE5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Linguini.Syntax", "Linguini\Linguini.Syntax\Linguini.Syntax.csproj", "{A5EDDBF2-3FA0-4364-A953-A814A0CBBE53}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluralRules.Generator", "Linguini\PluralRules.Generator\PluralRules.Generator.csproj", "{0641361F-37D7-40D8-A763-16896F67DC54}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluralRules.Generator", "Linguini\PluralRules.Generator\PluralRules.Generator.csproj", "{479FD643-665B-4DE1-B68E-3AAAD8E7A967}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -209,6 +221,46 @@ Global
{C60905B4-072F-4376-BCEC-C91186644127}.Release|Any CPU.Build.0 = Release|Any CPU
{C60905B4-072F-4376-BCEC-C91186644127}.Release|x64.ActiveCfg = Release|Any CPU
{C60905B4-072F-4376-BCEC-C91186644127}.Release|x64.Build.0 = Release|Any CPU
{71A3AD7B-9D09-4246-A574-E5EB7D7FF20B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71A3AD7B-9D09-4246-A574-E5EB7D7FF20B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71A3AD7B-9D09-4246-A574-E5EB7D7FF20B}.Debug|x64.ActiveCfg = Debug|Any CPU
{71A3AD7B-9D09-4246-A574-E5EB7D7FF20B}.Debug|x64.Build.0 = Debug|Any CPU
{71A3AD7B-9D09-4246-A574-E5EB7D7FF20B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71A3AD7B-9D09-4246-A574-E5EB7D7FF20B}.Release|Any CPU.Build.0 = Release|Any CPU
{71A3AD7B-9D09-4246-A574-E5EB7D7FF20B}.Release|x64.ActiveCfg = Release|Any CPU
{71A3AD7B-9D09-4246-A574-E5EB7D7FF20B}.Release|x64.Build.0 = Release|Any CPU
{918FEA70-4B6B-4C8D-8AAB-2C911DCDCAE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{918FEA70-4B6B-4C8D-8AAB-2C911DCDCAE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{918FEA70-4B6B-4C8D-8AAB-2C911DCDCAE5}.Debug|x64.ActiveCfg = Debug|Any CPU
{918FEA70-4B6B-4C8D-8AAB-2C911DCDCAE5}.Debug|x64.Build.0 = Debug|Any CPU
{918FEA70-4B6B-4C8D-8AAB-2C911DCDCAE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{918FEA70-4B6B-4C8D-8AAB-2C911DCDCAE5}.Release|Any CPU.Build.0 = Release|Any CPU
{918FEA70-4B6B-4C8D-8AAB-2C911DCDCAE5}.Release|x64.ActiveCfg = Release|Any CPU
{918FEA70-4B6B-4C8D-8AAB-2C911DCDCAE5}.Release|x64.Build.0 = Release|Any CPU
{A5EDDBF2-3FA0-4364-A953-A814A0CBBE53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A5EDDBF2-3FA0-4364-A953-A814A0CBBE53}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A5EDDBF2-3FA0-4364-A953-A814A0CBBE53}.Debug|x64.ActiveCfg = Debug|Any CPU
{A5EDDBF2-3FA0-4364-A953-A814A0CBBE53}.Debug|x64.Build.0 = Debug|Any CPU
{A5EDDBF2-3FA0-4364-A953-A814A0CBBE53}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A5EDDBF2-3FA0-4364-A953-A814A0CBBE53}.Release|Any CPU.Build.0 = Release|Any CPU
{A5EDDBF2-3FA0-4364-A953-A814A0CBBE53}.Release|x64.ActiveCfg = Release|Any CPU
{A5EDDBF2-3FA0-4364-A953-A814A0CBBE53}.Release|x64.Build.0 = Release|Any CPU
{0641361F-37D7-40D8-A763-16896F67DC54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0641361F-37D7-40D8-A763-16896F67DC54}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0641361F-37D7-40D8-A763-16896F67DC54}.Debug|x64.ActiveCfg = Debug|Any CPU
{0641361F-37D7-40D8-A763-16896F67DC54}.Debug|x64.Build.0 = Debug|Any CPU
{0641361F-37D7-40D8-A763-16896F67DC54}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0641361F-37D7-40D8-A763-16896F67DC54}.Release|Any CPU.Build.0 = Release|Any CPU
{0641361F-37D7-40D8-A763-16896F67DC54}.Release|x64.ActiveCfg = Release|Any CPU
{0641361F-37D7-40D8-A763-16896F67DC54}.Release|x64.Build.0 = Release|Any CPU
{479FD643-665B-4DE1-B68E-3AAAD8E7A967}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{479FD643-665B-4DE1-B68E-3AAAD8E7A967}.Debug|Any CPU.Build.0 = Debug|Any CPU
{479FD643-665B-4DE1-B68E-3AAAD8E7A967}.Debug|x64.ActiveCfg = Debug|Any CPU
{479FD643-665B-4DE1-B68E-3AAAD8E7A967}.Debug|x64.Build.0 = Debug|Any CPU
{479FD643-665B-4DE1-B68E-3AAAD8E7A967}.Release|Any CPU.ActiveCfg = Release|Any CPU
{479FD643-665B-4DE1-B68E-3AAAD8E7A967}.Release|Any CPU.Build.0 = Release|Any CPU
{479FD643-665B-4DE1-B68E-3AAAD8E7A967}.Release|x64.ActiveCfg = Release|Any CPU
{479FD643-665B-4DE1-B68E-3AAAD8E7A967}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -220,6 +272,10 @@ Global
{D73768A2-BFCD-4916-8F52-4034C28F345C} = {1B1FC7C4-0212-4B3E-90D4-C7B58759E4B0}
{1CDC9C4F-668E-47A3-8A44-216E95644BEB} = {1B1FC7C4-0212-4B3E-90D4-C7B58759E4B0}
{B05EFB71-AEC7-4C6E-984A-A1BCC58F9AD1} = {1B1FC7C4-0212-4B3E-90D4-C7B58759E4B0}
{71A3AD7B-9D09-4246-A574-E5EB7D7FF20B} = {3D0047D0-1089-48EC-AF72-A9B7A2E7A4BF}
{918FEA70-4B6B-4C8D-8AAB-2C911DCDCAE5} = {3D0047D0-1089-48EC-AF72-A9B7A2E7A4BF}
{A5EDDBF2-3FA0-4364-A953-A814A0CBBE53} = {3D0047D0-1089-48EC-AF72-A9B7A2E7A4BF}
{479FD643-665B-4DE1-B68E-3AAAD8E7A967} = {3D0047D0-1089-48EC-AF72-A9B7A2E7A4BF}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {57757344-0FF4-4842-8A68-141CAA18A35D}