CatStation/Content.Server/Corvax/GuideGenerator/LocJsonGenerator.cs

119 lines
4.1 KiB
C#

using System.IO;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.RegularExpressions;
using Robust.Shared.ContentPack;
using Robust.Shared.Utility;
namespace Content.Server.Corvax.GuideGenerator;
public static class LocJsonGenerator
{
private static readonly JsonSerializerOptions SerializeOptions = new()
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
};
// Matches top-level message/term identifiers at start of line (no leading whitespace or comment).
private static readonly Regex TopEntryRegex =
new(@"(?m)^(?!\s|#)([^\s=]+)\s*=", RegexOptions.Compiled | RegexOptions.CultureInvariant);
// Matches attribute lines like " .attr-name ="
private static readonly Regex AttrRegex =
new(@"(?m)^\s*\.(?<name>[A-Za-z0-9_\-]+)\s*=", RegexOptions.Compiled | RegexOptions.CultureInvariant);
private static string GetStringSafe(string key)
{
try
{
return Loc.GetString(key);
}
catch
{
return string.Empty;
}
}
public static void PublishJson(Stream stream)
{
var loc = IoCManager.Resolve<ILocalizationManager>();
var res = IoCManager.Resolve<IResourceManager>();
// Choose culture: default if set, otherwise first found culture.
var culture = loc.DefaultCulture ?? loc.GetFoundCultures().FirstOrDefault();
if (culture == null)
{
JsonSerializer.Serialize(stream, new Dictionary<string, object?>(), SerializeOptions);
return;
}
var root = new ResPath($"/Locale/{culture.Name}");
var files = res.ContentFindFiles(root)
.Where(c => c.Filename.EndsWith(".ftl", StringComparison.InvariantCultureIgnoreCase))
.ToArray();
var keys = new Dictionary<string, HashSet<string>>();
foreach (var path in files)
{
using var fileStream = res.ContentFileRead(path);
using var reader = new StreamReader(fileStream, Encoding.UTF8);
var contents = reader.ReadToEnd();
// Normalize line endings to simplify indexing.
contents = contents.Replace("\r\n", "\n");
var matches = TopEntryRegex.Matches(contents);
for (var mi = 0; mi < matches.Count; mi++)
{
var m = matches[mi];
var id = m.Groups[1].Value.Trim();
if (string.IsNullOrEmpty(id))
continue;
if (!keys.ContainsKey(id))
keys[id] = new HashSet<string>();
var start = m.Index;
var end = mi + 1 < matches.Count ? matches[mi + 1].Index : contents.Length;
var block = contents.Substring(start, end - start);
var attrMatches = AttrRegex.Matches(block);
foreach (Match am in attrMatches)
{
var attrName = am.Groups["name"].Value;
if (!string.IsNullOrEmpty(attrName))
keys[id].Add(attrName);
}
}
}
// Build JSON dictionary using Loc.GetString for main key and for attributes.
var output = new Dictionary<string, object?>();
foreach (var (id, attrs) in keys.OrderBy(k => k.Key))
{
if (attrs.Count == 0)
{
output[id] = GetStringSafe(id);
}
else
{
// _value is the main value of the key.
var obj = new Dictionary<string, string?>
{
["_value"] = GetStringSafe(id),
};
foreach (var attr in attrs.OrderBy(a => a))
{
var value = GetStringSafe($"{id}.{attr}");
if (!string.IsNullOrEmpty(value))
obj[attr] = value;
}
output[id] = obj;
}
}
JsonSerializer.Serialize(stream, output, SerializeOptions);
}
}