From eff710e3128780b67e7e79915d9730d00096824d Mon Sep 17 00:00:00 2001 From: Pok <113675512+Pok27@users.noreply.github.com> Date: Thu, 15 Jan 2026 08:44:45 +0200 Subject: [PATCH] [Wiki] loc fix 2 (#3484) --- .../Corvax/GuideGenerator/LocJsonGenerator.cs | 76 ++++++++++++++++--- 1 file changed, 64 insertions(+), 12 deletions(-) diff --git a/Content.Server/Corvax/GuideGenerator/LocJsonGenerator.cs b/Content.Server/Corvax/GuideGenerator/LocJsonGenerator.cs index 2ab4139b82..c309592d0d 100644 --- a/Content.Server/Corvax/GuideGenerator/LocJsonGenerator.cs +++ b/Content.Server/Corvax/GuideGenerator/LocJsonGenerator.cs @@ -29,19 +29,18 @@ public static class LocJsonGenerator .Where(c => c.Filename.EndsWith(".ftl", StringComparison.InvariantCultureIgnoreCase)) .ToArray(); var keys = new Dictionary>(); + var keysValues = new Dictionary(); // Matches top-level message/term identifiers at start of line (no leading whitespace or comment). - var topEntryRegex = new Regex(@"(?m)^(?!\s|#)([^\s=]+)\s*=", RegexOptions.Compiled); + var topEntryRegex = new Regex(@"^(?!\s|#)([^\s=]+)\s*=", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.CultureInvariant); // Matches attribute lines like " .attr-name =" - var attrRegex = new Regex(@"(?m)^\s*\.(?[A-Za-z0-9_\-]+)\s*=", RegexOptions.Compiled); + var attrRegex = new Regex(@"^\s*\.(?[A-Za-z0-9_\-]+)\s*=", RegexOptions.Compiled | RegexOptions.Multiline | RegexOptions.CultureInvariant); foreach (var path in files) { using var stream = res.ContentFileRead(path); using var reader = new StreamReader(stream, Encoding.UTF8); - var contents = reader.ReadToEnd(); - // Normalize line endings to simplify indexing. - contents = contents.Replace("\r\n", "\n"); + var contents = reader.ReadToEnd().Replace("\r\n", "\n"); var matches = topEntryRegex.Matches(contents); for (var mi = 0; mi < matches.Count; mi++) @@ -57,13 +56,66 @@ public static class LocJsonGenerator 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 mainEqual = block.IndexOf('='); + if (mainEqual >= 0) { + var mainValueStart = mainEqual + 1; + var mainValueEnd = attrMatches.Count > 0 ? attrMatches[0].Index : block.Length; + keysValues[id] = mainValueEnd > mainValueStart ? FluentValue(block.Substring(mainValueStart, mainValueEnd - mainValueStart)) : string.Empty; + } + else + { + keysValues[id] = string.Empty; + } + + for (var ai = 0; ai < attrMatches.Count; ai++) + { + var am = attrMatches[ai]; var attrName = am.Groups["name"].Value; - if (!string.IsNullOrEmpty(attrName)) - keys[id].Add(attrName); + if (string.IsNullOrEmpty(attrName)) + continue; + keys[id].Add(attrName); + + var attrEqual = block.IndexOf('=', am.Index); + var attrValueStart = attrEqual >= 0 ? attrEqual + 1 : am.Index + am.Length; + var nextAttrIndex = ai + 1 < attrMatches.Count ? attrMatches[ai + 1].Index : block.Length; + keysValues[$"{id}.{attrName}"] = nextAttrIndex > attrValueStart ? FluentValue(block.Substring(attrValueStart, nextAttrIndex - attrValueStart)) : string.Empty; + } + + continue; + + // Helper: remove leading newline and common indentation across non-empty lines. + string FluentValue(string val) + { + if (string.IsNullOrEmpty(val)) + return string.Empty; + + if (val.Length > 0 && val[0] == '\n') + val = val.Substring(1); + + var lines = val.Split('\n'); + lines = lines.Where(l => !(l.Length > 0 && l[0] == '#')).ToArray(); + var nonEmptyLines = lines.Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(); + if (nonEmptyLines.Length == 0) + return string.Join("\n", lines).TrimEnd('\n'); + + var minIndent = nonEmptyLines.Min(l => l.TakeWhile(char.IsWhiteSpace).Count()); + if (minIndent > 0) + { + for (var li = 0; li < lines.Length; li++) + { + var line = lines[li]; + if (line.Length >= minIndent) + lines[li] = line.Substring(minIndent); + } + } + + var result = string.Join("\n", lines); + if (result.EndsWith("\n")) + result = result.Substring(0, result.Length - 1); + return result; } } } @@ -74,18 +126,18 @@ public static class LocJsonGenerator { if (attrs.Count == 0) { - output[id] = Loc.GetString(id); + output[id] = keysValues.TryGetValue(id, out var value) ? value : string.Empty; } else { // _value is the main value of the key. var obj = new Dictionary { - ["_value"] = Loc.GetString(id), + ["_value"] = keysValues.TryGetValue(id, out var valueMain) ? valueMain : string.Empty, }; foreach (var attr in attrs.OrderBy(a => a)) { - obj[attr] = Loc.GetString($"{id}.{attr}"); + obj[attr] = keysValues.TryGetValue($"{id}.{attr}", out var valueAttr) ? valueAttr : string.Empty; } output[id] = obj; }