Remove magic ftl file naming.
This commit is contained in:
@@ -13,7 +13,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using Linguini.Bundle;
|
using Linguini.Bundle;
|
||||||
using Linguini.Bundle.Builder;
|
using Linguini.Bundle.Builder;
|
||||||
using Linguini.Shared.Types.Bundle;
|
using Linguini.Shared.Types.Bundle;
|
||||||
@@ -53,37 +52,30 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
readonly Linguini.Bundle.FluentBundle bundle;
|
readonly Linguini.Bundle.FluentBundle bundle;
|
||||||
|
|
||||||
public FluentBundle(string language, string[] paths, IReadOnlyFileSystem fileSystem)
|
public FluentBundle(string culture, string[] paths, IReadOnlyFileSystem fileSystem)
|
||||||
: this(language, paths, fileSystem, error => Log.Write("debug", error.Message)) { }
|
: this(culture, paths, fileSystem, error => Log.Write("debug", error.Message)) { }
|
||||||
|
|
||||||
public FluentBundle(string language, string[] paths, IReadOnlyFileSystem fileSystem, string text)
|
public FluentBundle(string culture, string[] paths, IReadOnlyFileSystem fileSystem, string text)
|
||||||
: this(language, paths, fileSystem, text, error => Log.Write("debug", error.Message)) { }
|
: this(culture, paths, fileSystem, text, error => Log.Write("debug", error.Message)) { }
|
||||||
|
|
||||||
public FluentBundle(string language, string[] paths, IReadOnlyFileSystem fileSystem, Action<ParseError> onError)
|
public FluentBundle(string culture, string[] paths, IReadOnlyFileSystem fileSystem, Action<ParseError> onError)
|
||||||
: this(language, paths, fileSystem, null, onError) { }
|
: this(culture, paths, fileSystem, null, onError) { }
|
||||||
|
|
||||||
public FluentBundle(string language, string text, Action<ParseError> onError)
|
public FluentBundle(string culture, string text, Action<ParseError> onError)
|
||||||
: this(language, null, null, text, onError) { }
|
: this(culture, null, null, text, onError) { }
|
||||||
|
|
||||||
public FluentBundle(string language, string[] paths, IReadOnlyFileSystem fileSystem, string text, Action<ParseError> onError)
|
public FluentBundle(string culture, string[] paths, IReadOnlyFileSystem fileSystem, string text, Action<ParseError> onError)
|
||||||
{
|
{
|
||||||
bundle = LinguiniBuilder.Builder()
|
bundle = LinguiniBuilder.Builder()
|
||||||
.CultureInfo(new CultureInfo(language))
|
.CultureInfo(new CultureInfo(culture))
|
||||||
.SkipResources()
|
.SkipResources()
|
||||||
.SetUseIsolating(false)
|
.SetUseIsolating(false)
|
||||||
.UseConcurrent()
|
.UseConcurrent()
|
||||||
.UncheckedBuild();
|
.UncheckedBuild();
|
||||||
|
|
||||||
if (paths != null && paths.Length > 0)
|
if (paths != null)
|
||||||
{
|
{
|
||||||
// Always load english strings to provide a fallback for missing translations.
|
foreach (var path in paths)
|
||||||
// It is important to load the english files first so the chosen language's files can override them.
|
|
||||||
var resolvedPaths = paths.Where(t => t.EndsWith("en.ftl", StringComparison.Ordinal)).ToList();
|
|
||||||
foreach (var t in paths)
|
|
||||||
if (t.EndsWith($"{language}.ftl", StringComparison.Ordinal))
|
|
||||||
resolvedPaths.Add(t);
|
|
||||||
|
|
||||||
foreach (var path in resolvedPaths.Distinct())
|
|
||||||
{
|
{
|
||||||
var stream = fileSystem.Open(path);
|
var stream = fileSystem.Open(path);
|
||||||
using (var reader = new StreamReader(stream))
|
using (var reader = new StreamReader(stream))
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace OpenRA
|
|||||||
{
|
{
|
||||||
lock (SyncObject)
|
lock (SyncObject)
|
||||||
{
|
{
|
||||||
modFluentBundle = new FluentBundle(Game.Settings.Player.Language, modData.Manifest.Translations, fileSystem);
|
modFluentBundle = new FluentBundle(modData.Manifest.FluentCulture, modData.Manifest.Translations, fileSystem);
|
||||||
if (fileSystem is Map map && map.FluentMessageDefinitions != null)
|
if (fileSystem is Map map && map.FluentMessageDefinitions != null)
|
||||||
{
|
{
|
||||||
var files = Array.Empty<string>();
|
var files = Array.Empty<string>();
|
||||||
@@ -44,7 +44,7 @@ namespace OpenRA
|
|||||||
text = builder.ToString();
|
text = builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
mapFluentBundle = new FluentBundle(Game.Settings.Player.Language, files, fileSystem, text);
|
mapFluentBundle = new FluentBundle(modData.Manifest.FluentCulture, files, fileSystem, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,10 +83,13 @@ namespace OpenRA
|
|||||||
public readonly string[] SpriteFormats = Array.Empty<string>();
|
public readonly string[] SpriteFormats = Array.Empty<string>();
|
||||||
public readonly string[] PackageFormats = Array.Empty<string>();
|
public readonly string[] PackageFormats = Array.Empty<string>();
|
||||||
public readonly string[] VideoFormats = Array.Empty<string>();
|
public readonly string[] VideoFormats = Array.Empty<string>();
|
||||||
public readonly bool AllowUnusedTranslationsInExternalPackages = true;
|
|
||||||
public readonly int FontSheetSize = 512;
|
public readonly int FontSheetSize = 512;
|
||||||
public readonly int CursorSheetSize = 512;
|
public readonly int CursorSheetSize = 512;
|
||||||
|
|
||||||
|
// TODO: This should be controlled by a user-selected translation bundle!
|
||||||
|
public readonly string FluentCulture = "en";
|
||||||
|
public readonly bool AllowUnusedTranslationsInExternalPackages = true;
|
||||||
|
|
||||||
readonly string[] reservedModuleNames =
|
readonly string[] reservedModuleNames =
|
||||||
{
|
{
|
||||||
"Include", "Metadata", "FileSystem", "MapFolders", "Rules",
|
"Include", "Metadata", "FileSystem", "MapFolders", "Rules",
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ namespace OpenRA
|
|||||||
text = builder.ToString();
|
text = builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
FluentBundle = new FluentBundle(Game.Settings.Player.Language, files, fileSystem, text);
|
FluentBundle = new FluentBundle(modData.Manifest.FluentCulture, files, fileSystem, text);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
FluentBundle = null;
|
FluentBundle = null;
|
||||||
|
|||||||
@@ -249,7 +249,6 @@ namespace OpenRA
|
|||||||
public Color Color = Color.FromArgb(200, 32, 32);
|
public Color Color = Color.FromArgb(200, 32, 32);
|
||||||
public string LastServer = "localhost:1234";
|
public string LastServer = "localhost:1234";
|
||||||
public Color[] CustomColors = Array.Empty<Color>();
|
public Color[] CustomColors = Array.Empty<Color>();
|
||||||
public string Language = "en";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class GameSettings
|
public class GameSettings
|
||||||
|
|||||||
@@ -26,9 +26,6 @@ namespace OpenRA
|
|||||||
if (Game.EngineVersion != null)
|
if (Game.EngineVersion != null)
|
||||||
Log.Write("exception", $"OpenRA engine version {Game.EngineVersion}");
|
Log.Write("exception", $"OpenRA engine version {Game.EngineVersion}");
|
||||||
|
|
||||||
if (Game.Settings != null && Game.Settings.Player != null && Game.Settings.Player.Language != null)
|
|
||||||
Log.Write("exception", $"OpenRA Language: {Game.Settings.Player.Language}");
|
|
||||||
|
|
||||||
if (Game.ModData != null)
|
if (Game.ModData != null)
|
||||||
{
|
{
|
||||||
var manifest = Game.ModData.Manifest;
|
var manifest = Game.ModData.Manifest;
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ namespace OpenRA.Mods.Common.Lint
|
|||||||
{
|
{
|
||||||
sealed class CheckFluentReferences : ILintPass, ILintMapPass
|
sealed class CheckFluentReferences : ILintPass, ILintMapPass
|
||||||
{
|
{
|
||||||
static readonly Regex FilenameRegex = new(@"(?<language>[^\/\\]+)\.ftl$");
|
|
||||||
|
|
||||||
void ILintMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Map map)
|
void ILintMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, Map map)
|
||||||
{
|
{
|
||||||
if (map.FluentMessageDefinitions == null)
|
if (map.FluentMessageDefinitions == null)
|
||||||
@@ -43,32 +41,27 @@ namespace OpenRA.Mods.Common.Lint
|
|||||||
emitWarning($"Empty key in map ftl files required by {context}");
|
emitWarning($"Empty key in map ftl files required by {context}");
|
||||||
|
|
||||||
var mapTranslations = FieldLoader.GetValue<string[]>("value", map.FluentMessageDefinitions.Value);
|
var mapTranslations = FieldLoader.GetValue<string[]>("value", map.FluentMessageDefinitions.Value);
|
||||||
|
|
||||||
var allModTranslations = modData.Manifest.Translations;
|
var allModTranslations = modData.Manifest.Translations;
|
||||||
foreach (var language in GetModLanguages(allModTranslations))
|
|
||||||
|
// For maps we don't warn on unused keys. They might be unused on *this* map,
|
||||||
|
// but the mod or another map may use them and we don't have sight of that.
|
||||||
|
CheckKeys(allModTranslations.Concat(mapTranslations), map.Open, usedKeys,
|
||||||
|
_ => false, emitError, emitWarning);
|
||||||
|
|
||||||
|
var modFluentBundle = new FluentBundle(modData.Manifest.FluentCulture, allModTranslations, modData.DefaultFileSystem, _ => { });
|
||||||
|
var mapFluentBundle = new FluentBundle(modData.Manifest.FluentCulture, mapTranslations, map, error => emitError(error.Message));
|
||||||
|
|
||||||
|
foreach (var group in usedKeys.KeysWithContext)
|
||||||
{
|
{
|
||||||
// Check keys and variables are not missing across all language files.
|
if (modFluentBundle.HasMessage(group.Key))
|
||||||
// But for maps we don't warn on unused keys. They might be unused on *this* map,
|
|
||||||
// but the mod or another map may use them and we don't have sight of that.
|
|
||||||
CheckKeys(
|
|
||||||
allModTranslations.Concat(mapTranslations), map.Open, usedKeys,
|
|
||||||
language, _ => false, emitError, emitWarning);
|
|
||||||
|
|
||||||
var modFluentBundle = new FluentBundle(language, allModTranslations, modData.DefaultFileSystem, _ => { });
|
|
||||||
var mapFluentBundle = new FluentBundle(language, mapTranslations, map, error => emitError(error.Message));
|
|
||||||
|
|
||||||
foreach (var group in usedKeys.KeysWithContext)
|
|
||||||
{
|
{
|
||||||
if (modFluentBundle.HasMessage(group.Key))
|
if (mapFluentBundle.HasMessage(group.Key))
|
||||||
{
|
emitWarning($"Key `{group.Key}` in map ftl files already exists in mod translations and will not be used.");
|
||||||
if (mapFluentBundle.HasMessage(group.Key))
|
}
|
||||||
emitWarning($"Key `{group.Key}` in `{language}` language in map ftl files already exists in mod translations and will not be used.");
|
else if (!mapFluentBundle.HasMessage(group.Key))
|
||||||
}
|
{
|
||||||
else if (!mapFluentBundle.HasMessage(group.Key))
|
foreach (var context in group)
|
||||||
{
|
emitWarning($"Missing key `{group.Key}` in map ftl files required by {context}");
|
||||||
foreach (var context in group)
|
|
||||||
emitWarning($"Missing key `{group.Key}` in `{language}` language in map ftl files required by {context}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,34 +73,30 @@ namespace OpenRA.Mods.Common.Lint
|
|||||||
|
|
||||||
void ILintPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData)
|
void ILintPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine("Testing Fluent references");
|
||||||
var (usedKeys, testedFields) = GetUsedFluentKeysInMod(modData);
|
var (usedKeys, testedFields) = GetUsedFluentKeysInMod(modData);
|
||||||
|
|
||||||
foreach (var context in usedKeys.EmptyKeyContexts)
|
foreach (var context in usedKeys.EmptyKeyContexts)
|
||||||
emitWarning($"Empty key in mod translation files required by {context}");
|
emitWarning($"Empty key in mod translation files required by {context}");
|
||||||
|
|
||||||
var allModTranslations = modData.Manifest.Translations.ToArray();
|
var allModTranslations = modData.Manifest.Translations.ToArray();
|
||||||
foreach (var language in GetModLanguages(allModTranslations))
|
CheckModWidgets(modData, usedKeys, testedFields);
|
||||||
|
|
||||||
|
// With the fully populated keys, check keys and variables are not missing and not unused across all language files.
|
||||||
|
var keyWithAttrs = CheckKeys(
|
||||||
|
allModTranslations, modData.DefaultFileSystem.Open, usedKeys,
|
||||||
|
file =>
|
||||||
|
!modData.Manifest.AllowUnusedTranslationsInExternalPackages ||
|
||||||
|
!modData.DefaultFileSystem.IsExternalFile(file),
|
||||||
|
emitError, emitWarning);
|
||||||
|
|
||||||
|
foreach (var group in usedKeys.KeysWithContext)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"Testing language: {language}");
|
if (keyWithAttrs.Contains(group.Key))
|
||||||
CheckModWidgets(modData, usedKeys, testedFields);
|
continue;
|
||||||
|
|
||||||
// With the fully populated keys, check keys and variables are not missing and not unused across all language files.
|
foreach (var context in group)
|
||||||
var keyWithAttrs = CheckKeys(
|
emitWarning($"Missing key `{group.Key}` in mod ftl files required by {context}");
|
||||||
allModTranslations, modData.DefaultFileSystem.Open, usedKeys,
|
|
||||||
language,
|
|
||||||
file =>
|
|
||||||
!modData.Manifest.AllowUnusedTranslationsInExternalPackages ||
|
|
||||||
!modData.DefaultFileSystem.IsExternalFile(file),
|
|
||||||
emitError, emitWarning);
|
|
||||||
|
|
||||||
foreach (var group in usedKeys.KeysWithContext)
|
|
||||||
{
|
|
||||||
if (keyWithAttrs.Contains(group.Key))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
foreach (var context in group)
|
|
||||||
emitWarning($"Missing key `{group.Key}` in `{language}` language in mod ftl files required by {context}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we couldn't test any fields.
|
// Check if we couldn't test any fields.
|
||||||
@@ -121,14 +110,6 @@ namespace OpenRA.Mods.Common.Lint
|
|||||||
$"`{field.ReflectedType.Name}.{field.Name}` - previous warnings may be incorrect");
|
$"`{field.ReflectedType.Name}.{field.Name}` - previous warnings may be incorrect");
|
||||||
}
|
}
|
||||||
|
|
||||||
static IEnumerable<string> GetModLanguages(IEnumerable<string> translations)
|
|
||||||
{
|
|
||||||
return translations
|
|
||||||
.Select(filename => FilenameRegex.Match(filename).Groups["language"].Value)
|
|
||||||
.Distinct()
|
|
||||||
.OrderBy(l => l);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Keys GetUsedFluentKeysInRuleset(Ruleset rules)
|
static Keys GetUsedFluentKeysInRuleset(Ruleset rules)
|
||||||
{
|
{
|
||||||
var usedKeys = new Keys();
|
var usedKeys = new Keys();
|
||||||
@@ -427,15 +408,11 @@ namespace OpenRA.Mods.Common.Lint
|
|||||||
|
|
||||||
static HashSet<string> CheckKeys(
|
static HashSet<string> CheckKeys(
|
||||||
IEnumerable<string> paths, Func<string, Stream> openFile, Keys usedKeys,
|
IEnumerable<string> paths, Func<string, Stream> openFile, Keys usedKeys,
|
||||||
string language, Func<string, bool> checkUnusedKeysForFile,
|
Func<string, bool> checkUnusedKeysForFile, Action<string> emitError, Action<string> emitWarning)
|
||||||
Action<string> emitError, Action<string> emitWarning)
|
|
||||||
{
|
{
|
||||||
var keyWithAttrs = new HashSet<string>();
|
var keyWithAttrs = new HashSet<string>();
|
||||||
foreach (var path in paths)
|
foreach (var path in paths)
|
||||||
{
|
{
|
||||||
if (!path.EndsWith($"{language}.ftl", StringComparison.Ordinal))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var stream = openFile(path);
|
var stream = openFile(path);
|
||||||
using (var reader = new StreamReader(stream))
|
using (var reader = new StreamReader(stream))
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user