diff --git a/.github/workflows/update-wiki.yml b/.github/workflows/update-wiki.yml index 898e694934..ddd21f2f0b 100644 --- a/.github/workflows/update-wiki.yml +++ b/.github/workflows/update-wiki.yml @@ -14,6 +14,7 @@ on: - 'Resources/Prototypes/Chemistry/**.yml' - 'Resources/Prototypes/Recipes/Reactions/**.yml' - 'RobustToolbox/' + - 'Resources/Locale/**.ftl' jobs: update-wiki: @@ -90,3 +91,13 @@ jobs: api_url: ${{ secrets.WIKI_ROOT_URL }}/api.php username: ${{ secrets.WIKI_BOT_USER }} password: ${{ secrets.WIKI_BOT_PASS }} + + - name: Upload loc.json to wiki + uses: jtmullen/mediawiki-edit-action@v0.1.1 + with: + wiki_text_file: ./bin/Content.Server/data/loc.json + edit_summary: Update loc.json via GitHub Actions + page_name: "${{ secrets.WIKI_PAGE_ROOT }}/loc.json" + api_url: ${{ secrets.WIKI_ROOT_URL }}/api.php + username: ${{ secrets.WIKI_BOT_USER }} + password: ${{ secrets.WIKI_BOT_PASS }} diff --git a/Content.Server/Corvax/GuideGenerator/LocJsonGenerator.cs b/Content.Server/Corvax/GuideGenerator/LocJsonGenerator.cs new file mode 100644 index 0000000000..8eb9cb4bf3 --- /dev/null +++ b/Content.Server/Corvax/GuideGenerator/LocJsonGenerator.cs @@ -0,0 +1,100 @@ +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Text.Json; +using Robust.Shared.ContentPack; +using Robust.Shared.Utility; + +namespace Content.Server.GuideGenerator; + +public static class LocJsonGenerator +{ + public static void PublishJson(StreamWriter file) + { + var loc = IoCManager.Resolve(); + var res = IoCManager.Resolve(); + + // Choose culture: default if set, otherwise first found culture. + var culture = loc.DefaultCulture ?? loc.GetFoundCultures().FirstOrDefault(); + if (culture == null) + { + file.Write("{}"); + 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>(); + + // 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); + // Matches attribute lines like " .attr-name =" + var attrRegex = new Regex(@"(?m)^\s*\.(?[A-Za-z0-9_\-]+)\s*=", RegexOptions.Compiled); + + 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 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(); + + 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(); + foreach (var (id, attrs) in keys.OrderBy(k => k.Key)) + { + if (attrs.Count == 0) + { + output[id] = Loc.GetString(id); + } + else + { + // _value is the main value of the key. + var obj = new Dictionary + { + ["_value"] = Loc.GetString(id), + }; + foreach (var attr in attrs.OrderBy(a => a)) + { + obj[attr] = Loc.GetString($"{id}.{attr}"); + } + output[id] = obj; + } + } + + var serializeOptions = new JsonSerializerOptions + { + WriteIndented = true, + }; + + file.Write(JsonSerializer.Serialize(output, serializeOptions)); + } +} diff --git a/Content.Server/Entry/EntryPoint.cs b/Content.Server/Entry/EntryPoint.cs index 63ce216e97..350e8e8b91 100644 --- a/Content.Server/Entry/EntryPoint.cs +++ b/Content.Server/Entry/EntryPoint.cs @@ -162,6 +162,9 @@ namespace Content.Server.Entry file = _res.UserData.OpenWriteText(resPath.WithName("healthchangereagents_" + dest)); HealthChangeReagentsJsonGenerator.PublishJson(file); file.Flush(); + file = _res.UserData.OpenWriteText(resPath.WithName("loc.json")); + LocJsonGenerator.PublishJson(file); + file.Flush(); // Corvax-Wiki-End Dependencies.Resolve().Shutdown("Data generation done"); return;