From e251377f7c2bc7884cd31ce63ca13e1e10aabc42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Mail=C3=A4nder?= Date: Tue, 25 Oct 2022 21:31:36 +0200 Subject: [PATCH] Add support for translation attributes. --- OpenRA.Game/OpenRA.Game.csproj | 2 +- OpenRA.Game/Translation.cs | 4 +- .../Lint/CheckTranslationReference.cs | 129 +++++++++++------- 3 files changed, 84 insertions(+), 51 deletions(-) diff --git a/OpenRA.Game/OpenRA.Game.csproj b/OpenRA.Game/OpenRA.Game.csproj index 6de6403acf..86a7f37163 100644 --- a/OpenRA.Game/OpenRA.Game.csproj +++ b/OpenRA.Game/OpenRA.Game.csproj @@ -7,7 +7,7 @@ - + diff --git a/OpenRA.Game/Translation.cs b/OpenRA.Game/Translation.cs index 91b2f7b916..9e29c3be58 100644 --- a/OpenRA.Game/Translation.cs +++ b/OpenRA.Game/Translation.cs @@ -101,7 +101,7 @@ namespace OpenRA foreach (var (k, v) in arguments) fluentArguments.Add(k, v.ToFluentType()); - var result = bundle.TryGetAttrMsg(key, fluentArguments, out var errors, out value); + var result = bundle.TryGetAttrMessage(key, fluentArguments, out var errors, out value); foreach (var error in errors) Log.Write("debug", $"Translation of {key}: {error}"); @@ -118,7 +118,7 @@ namespace OpenRA public bool HasMessage(string key) { - return bundle.HasMessage(key); + return bundle.HasAttrMessage(key); } // Adapted from Fluent.Net.SimpleExample.TranslationService by Mark Weaver diff --git a/OpenRA.Mods.Common/Lint/CheckTranslationReference.cs b/OpenRA.Mods.Common/Lint/CheckTranslationReference.cs index 1cf5fa915b..272d51a829 100644 --- a/OpenRA.Mods.Common/Lint/CheckTranslationReference.cs +++ b/OpenRA.Mods.Common/Lint/CheckTranslationReference.cs @@ -62,54 +62,31 @@ namespace OpenRA.Mods.Common.Lint foreach (var entry in result.Entries) { // Don't flag definitions referenced (only) within the .ftl definitions as unused - var isReusableTerm = entry.GetType() == typeof(AstTerm); - var key = entry.GetId(); - if (!referencedKeys.Contains(key) && !isReusableTerm) - emitWarning($"Unused key `{key}` in {file}."); + if (entry.GetType() == typeof(AstTerm)) + continue; variableReferences.Clear(); + var key = entry.GetId(); + if (entry is AstMessage message) { - var node = message.Value; - foreach (var element in node.Elements) - { - if (element is Placeable placeable) - { - var expression = placeable.Expression; - if (expression is IInlineExpression inlineExpression) - { - if (inlineExpression is VariableReference variableReference) - CheckVariableReference(variableReference.Id.Name.ToString(), entry, emitWarning, file); - } + var hasAttributes = message.Attributes.Count > 0; - if (expression is SelectExpression selectExpression) - { - foreach (var variant in selectExpression.Variants) - { - foreach (var variantElement in variant.Value.Elements) - { - if (variantElement is Placeable variantPlaceable) - { - var variantExpression = variantPlaceable.Expression; - if (variantExpression is IInlineExpression variantInlineExpression) - { - if (variantInlineExpression is VariableReference variantVariableReference) - CheckVariableReference(variantVariableReference.Id.Name.ToString(), entry, emitWarning, file); - } - } - } - } - } - } + if (!hasAttributes) + { + CheckUnusedKey(key, null, emitWarning, file); + CheckMessageValue(message.Value, key, null, emitWarning, file); + CheckMissingVariable(key, null, emitError, file); } - - if (referencedVariablesPerKey.ContainsKey(entry.GetId())) + else { - var referencedVariables = referencedVariablesPerKey[entry.GetId()]; - foreach (var referencedVariable in referencedVariables) + foreach (var attribute in message.Attributes) { - if (!variableReferences.Contains(referencedVariable)) - emitError($"Missing variable `{referencedVariable}` for key `{entry.GetId()}` in {file}."); + var attrName = attribute.Id.Name.ToString(); + + CheckUnusedKey(key, attrName, emitWarning, file); + CheckMessageValue(attribute.Value, key, attrName, emitWarning, file); + CheckMissingVariable(key, attrName, emitError, file); } } } @@ -118,18 +95,74 @@ namespace OpenRA.Mods.Common.Lint } } - void CheckVariableReference(string element, IEntry entry, Action emitWarning, string file) + void CheckUnusedKey(string key, string attribute, Action emitWarning, string file) { - variableReferences.Add(element); + var isAttribute = !string.IsNullOrEmpty(attribute); + var keyWithAtrr = isAttribute ? $"{key}.{attribute}" : key; - if (referencedVariablesPerKey.ContainsKey(entry.GetId())) + if (!referencedKeys.Contains(keyWithAtrr)) + emitWarning(isAttribute ? + $"Unused attribute `{attribute}` of key `{key}` in {file}." : + $"Unused key `{key}` in {file}."); + } + + void CheckMessageValue(Pattern node, string key, string attribute, Action emitWarning, string file) + { + foreach (var element in node.Elements) { - var referencedVariables = referencedVariablesPerKey[entry.GetId()]; - if (!referencedVariables.Contains(element)) - emitWarning($"Unused variable `{element}` for key `{entry.GetId()}` in {file}."); + if (element is Placeable placeable) + { + var expression = placeable.Expression; + if (expression is IInlineExpression inlineExpression) + if (inlineExpression is VariableReference variableReference) + CheckVariableReference(variableReference.Id.Name.ToString(), key, attribute, emitWarning, file); + + if (expression is SelectExpression selectExpression) + { + foreach (var variant in selectExpression.Variants) + { + foreach (var variantElement in variant.Value.Elements) + { + if (variantElement is Placeable variantPlaceable) + { + var variantExpression = variantPlaceable.Expression; + if (variantExpression is IInlineExpression variantInlineExpression) + if (variantInlineExpression is VariableReference variantVariableReference) + CheckVariableReference(variantVariableReference.Id.Name.ToString(), key, attribute, emitWarning, file); + } + } + } + } + } } - else - emitWarning($"Unused variable `{element}` for key `{entry.GetId()}` in {file}."); + } + + void CheckMissingVariable(string key, string attribute, Action emitError, string file) + { + var isAttribute = !string.IsNullOrEmpty(attribute); + var keyWithAtrr = isAttribute ? $"{key}.{attribute}" : key; + + if (!referencedVariablesPerKey.ContainsKey(keyWithAtrr)) + return; + + foreach (var referencedVariable in referencedVariablesPerKey[keyWithAtrr]) + if (!variableReferences.Contains(referencedVariable)) + emitError(isAttribute ? + $"Missing variable `{referencedVariable}` for attribute `{attribute}` of key `{key}` in {file}." : + $"Missing variable `{referencedVariable}` for key `{key}` in {file}."); + } + + void CheckVariableReference(string varName, string key, string attribute, Action emitWarning, string file) + { + var isAttribute = !string.IsNullOrEmpty(attribute); + var keyWithAtrr = isAttribute ? $"{key}.{attribute}" : key; + + variableReferences.Add(varName); + + if (!referencedVariablesPerKey.ContainsKey(keyWithAtrr) || !referencedVariablesPerKey[keyWithAtrr].Contains(varName)) + emitWarning(isAttribute ? + $"Unused variable `{varName}` for attribute `{attribute}` of key `{key}` in {file}." : + $"Unused variable `{varName}` for key `{key}` in {file}."); } } }