Rename Fluent-related code to be more precise.

This commit is contained in:
Paul Chote
2024-10-01 19:34:12 +01:00
committed by Gustas
parent 771b9ddfda
commit b29b685058
176 changed files with 1349 additions and 1369 deletions

View File

@@ -11,7 +11,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
@@ -37,11 +36,11 @@ namespace OpenRA.Mods.Common.UtilityCommands
// HACK: The engine code assumes that Game.modData is set.
var modData = Game.ModData = utility.ModData;
var translatableFields = modData.ObjectCreator.GetTypes()
var widgetInfos = modData.ObjectCreator.GetTypes()
.Where(t => t.Name.EndsWith("Widget", StringComparison.InvariantCulture) && t.IsSubclassOf(typeof(Widget)))
.ToDictionary(
t => t.Name[..^6],
t => t.GetFields().Where(f => f.HasAttribute<TranslationReferenceAttribute>()).Select(f => f.Name).ToArray())
t => t.GetFields().Where(f => f.HasAttribute<FluentReferenceAttribute>()).Select(f => f.Name).ToArray())
.Where(t => t.Value.Length > 0)
.ToDictionary(t => t.Key, t => t.Value);
@@ -53,12 +52,12 @@ namespace OpenRA.Mods.Common.UtilityCommands
var fluentPackage = modData.ModFiles.OpenPackage(fluentFolder);
var fluentPath = Path.Combine(fluentPackage.Name, "chrome/en.ftl");
var unsortedCandidates = new List<TranslationCandidate>();
var groupedCandidates = new Dictionary<HashSet<string>, List<TranslationCandidate>>();
var unsortedCandidates = new List<ExtractionCandidate>();
var groupedCandidates = new Dictionary<HashSet<string>, List<ExtractionCandidate>>();
var yamlSet = new YamlFileSet();
// Get all translations.
// Get all string candidates.
foreach (var chrome in layout)
{
modData.ModFiles.TryGetPackageContaining(chrome, out var chromePackage, out var chromeName);
@@ -67,40 +66,40 @@ namespace OpenRA.Mods.Common.UtilityCommands
var yaml = MiniYaml.FromFile(chromePath, false).ConvertAll(n => new MiniYamlNodeBuilder(n));
yamlSet.Add(((IReadWritePackage)chromePackage, chromeName, yaml));
var translationCandidates = new List<TranslationCandidate>();
var extractionCandidates = new List<ExtractionCandidate>();
foreach (var node in yaml)
{
if (node.Key != null)
{
var nodeSplit = node.Key.Split('@');
var nodeId = nodeSplit.Length > 1 ? ClearContainersAndToLower(nodeSplit[1]) : null;
FromChromeLayout(node, translatableFields, nodeId, ref translationCandidates);
FromChromeLayout(node, widgetInfos, nodeId, ref extractionCandidates);
}
}
if (translationCandidates.Count > 0)
if (extractionCandidates.Count > 0)
{
var chromeFilename = chrome.Split('/').Last();
groupedCandidates[new HashSet<string>() { chromeFilename }] = new List<TranslationCandidate>();
for (var i = 0; i < translationCandidates.Count; i++)
groupedCandidates[new HashSet<string>() { chromeFilename }] = new List<ExtractionCandidate>();
for (var i = 0; i < extractionCandidates.Count; i++)
{
var candidate = translationCandidates[i];
var candidate = extractionCandidates[i];
candidate.Chrome = chromeFilename;
unsortedCandidates.Add(candidate);
}
}
}
// Join matching translations.
// Join matching candidates.
foreach (var candidate in unsortedCandidates)
{
HashSet<string> foundHash = null;
TranslationCandidate found = default;
foreach (var (hash, translation) in groupedCandidates)
ExtractionCandidate found = default;
foreach (var (hash, candidates) in groupedCandidates)
{
foreach (var c in translation)
foreach (var c in candidates)
{
if (c.Key == candidate.Key && c.Type == candidate.Type && c.Translation == candidate.Translation)
if (c.Key == candidate.Key && c.Type == candidate.Type && c.Value == candidate.Value)
{
foundHash = hash;
found = c;
@@ -127,7 +126,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
if (nHash.Key != null)
groupedCandidates[nHash.Key].Add(candidate);
else
groupedCandidates[newHash] = new List<TranslationCandidate>() { candidate };
groupedCandidates[newHash] = new List<ExtractionCandidate>() { candidate };
}
var startWithNewline = File.Exists(fluentPath);
@@ -136,7 +135,7 @@ namespace OpenRA.Mods.Common.UtilityCommands
if (!startWithNewline)
Directory.CreateDirectory(Path.GetDirectoryName(fluentPath));
// Write to translation files.
// Write output .ftl files.
using (var fluentWriter = new StreamWriter(fluentPath, append: true))
{
foreach (var (chromeFilename, candidates) in groupedCandidates.OrderBy(t => string.Join(',', t.Key)))
@@ -151,30 +150,28 @@ namespace OpenRA.Mods.Common.UtilityCommands
fluentWriter.WriteLine("## " + string.Join(", ", chromeFilename));
// Pushing blocks of translations to string first allows for fancier formatting.
// Pushing blocks to string first allows for fancier formatting.
var build = "";
foreach (var grouping in candidates.GroupBy(t => t.Key))
{
if (grouping.Count() == 1)
{
var candidate = grouping.First();
var translationKey = candidate.Key;
if (candidate.Type == "text")
translationKey = $"{translationKey}";
else
translationKey = $"{translationKey}-" + candidate.Type.Replace("text", "");
var key = candidate.Key;
if (candidate.Type != "text")
key = $"{key}-" + candidate.Type.Replace("text", "");
build += $"{translationKey} = {candidate.Translation}\n";
build += $"{key} = {candidate.Value}\n";
foreach (var node in candidate.Nodes)
node.Value.Value = translationKey;
node.Value.Value = key;
}
else
{
if (build.Length > 1 && build.Substring(build.Length - 2, 2) != "\n\n")
build += "\n";
var translationKey = grouping.Key;
build += $"{translationKey} =\n";
var key = grouping.Key;
build += $"{key} =\n";
foreach (var candidate in grouping)
{
var type = candidate.Type;
@@ -186,9 +183,9 @@ namespace OpenRA.Mods.Common.UtilityCommands
type = type.Replace("text", "");
}
build += $" .{type} = {candidate.Translation}\n";
build += $" .{type} = {candidate.Value}\n";
foreach (var node in candidate.Nodes)
node.Value.Value = $"{translationKey}.{type}";
node.Value.Value = $"{key}.{type}";
}
build += "\n";
@@ -203,20 +200,20 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
}
struct TranslationCandidate
struct ExtractionCandidate
{
public string Chrome;
public readonly string Key;
public readonly string Type;
public readonly string Translation;
public readonly string Value;
public readonly List<MiniYamlNodeBuilder> Nodes;
public TranslationCandidate(string key, string type, string translation, MiniYamlNodeBuilder node)
public ExtractionCandidate(string key, string type, string value, MiniYamlNodeBuilder node)
{
Chrome = null;
Key = key;
Type = type;
Translation = translation;
Value = value;
Nodes = new List<MiniYamlNodeBuilder>() { node };
}
}
@@ -245,88 +242,69 @@ namespace OpenRA.Mods.Common.UtilityCommands
}
static void FromChromeLayout(
MiniYamlNodeBuilder node, Dictionary<string, string[]> translatables, string container, ref List<TranslationCandidate> translations)
MiniYamlNodeBuilder node, Dictionary<string, string[]> widgetInfos, string container, ref List<ExtractionCandidate> candidates)
{
var nodeSplit = node.Key.Split('@');
var nodeType = nodeSplit[0];
var widgetType = nodeSplit[0];
var nodeId = nodeSplit.Length > 1 ? ClearContainersAndToLower(nodeSplit[1]) : null;
if ((nodeType == "Background" || nodeType == "Container") && nodeId != null)
if ((widgetType == "Background" || widgetType == "Container") && nodeId != null)
container = nodeId;
// Get translatable types.
var validChildTypes = new List<(MiniYamlNodeBuilder Node, string Type, string Value)>();
foreach (var childNode in node.Value.Nodes)
{
if (translatables.TryGetValue(nodeType, out var fieldName))
if (widgetInfos.TryGetValue(widgetType, out var fieldName))
{
var childType = childNode.Key.Split('@')[0];
if (fieldName.Contains(childType)
&& !string.IsNullOrEmpty(childNode.Value.Value)
&& !UpdateUtils.IsAlreadyTranslated(childNode.Value.Value)
&& !UpdateUtils.IsAlreadyExtracted(childNode.Value.Value)
&& childNode.Value.Value.Any(char.IsLetterOrDigit))
{
var translationValue = childNode.Value.Value
var value = childNode.Value.Value
.Replace("\\n", "\n ")
.Replace("{", "<")
.Replace("}", ">")
.Trim().Trim('\n');
validChildTypes.Add((childNode, childType.ToLowerInvariant(), translationValue));
validChildTypes.Add((childNode, childType.ToLowerInvariant(), value));
}
}
}
// Generate translation key.
// Generate string key.
if (validChildTypes.Count > 0)
{
nodeType = ClearTypesAndToLower(nodeType);
widgetType = ClearTypesAndToLower(widgetType);
var translationKey = nodeType;
var key = widgetType;
if (!string.IsNullOrEmpty(container))
{
var containerType = string.Join('-', container.Split('_').Exclude(nodeType).Where(s => !string.IsNullOrEmpty(s)));
var containerType = string.Join('-', container.Split('_').Exclude(widgetType).Where(s => !string.IsNullOrEmpty(s)));
if (!string.IsNullOrEmpty(containerType))
translationKey = $"{translationKey}-{containerType}";
key = $"{key}-{containerType}";
}
if (!string.IsNullOrEmpty(nodeId))
{
nodeId = string.Join('-', nodeId.Split('_')
.Except(string.IsNullOrEmpty(container) ? new string[] { nodeType } : container.Split('_').Append(nodeType))
.Except(string.IsNullOrEmpty(container) ? new string[] { widgetType } : container.Split('_').Append(widgetType))
.Where(s => !string.IsNullOrEmpty(s)));
if (!string.IsNullOrEmpty(nodeId))
translationKey = $"{translationKey}-{nodeId}";
key = $"{key}-{nodeId}";
}
foreach (var (childNode, childType, translationValue) in validChildTypes)
translations.Add(new TranslationCandidate(translationKey, childType, translationValue.Trim().Trim('\n'), childNode));
foreach (var (childNode, childType, childValue) in validChildTypes)
candidates.Add(new ExtractionCandidate(key, childType, childValue.Trim().Trim('\n'), childNode));
}
// Recursive.
foreach (var childNode in node.Value.Nodes)
if (childNode.Key == "Children")
foreach (var n in childNode.Value.Nodes)
FromChromeLayout(n, translatables, container, ref translations);
}
/// <summary>This is a helper method to find untranslated strings in chrome layouts.</summary>
public static void FindUntranslatedStringFields(ModData modData)
{
var types = modData.ObjectCreator.GetTypes();
foreach (var (type, fields) in types
.Where(t => t.Name.EndsWith("Widget", StringComparison.InvariantCulture) && t.IsSubclassOf(typeof(Widget)))
.ToDictionary(
t => t.Name[..^6],
t => t
.GetFields()
.Where(f => f.Name != "Id" && f.IsPublic && f.FieldType == typeof(string) && !f.HasAttribute<TranslationReferenceAttribute>())
.Distinct()
.Select(f => f.Name)
.ToList()))
if (fields.Count > 0)
Console.WriteLine($"{type}Widget:\n {string.Join("\n ", fields)}");
FromChromeLayout(n, widgetInfos, container, ref candidates);
}
}
}