Strip all comments from the C# sources

Removes every // and /// comment from the 28 .cs files, -1331 lines.

Done with a lexer rather than a regex: the sources carry plenty of // inside
string literals (@"//\$decor\b", the //$o trainset directives, sb.Append("// "),
"https://wiki.eu07.pl/") and those must survive. It tracks regular, verbatim and
interpolated strings, interpolation holes and char literals. Every file was
checked by comparing the sequence of string-literal characters before and after
the strip; all 28 match. BOMs preserved, blank-line runs collapsed to one.

Claude-Session: https://claude.ai/code/session_01HamVNWE9mwmz1vs2iEpNQq
This commit is contained in:
maj00r
2026-08-29 16:41:22 +02:00
parent 969a5b9494
commit 3b7a8135ea
28 changed files with 274 additions and 1288 deletions

View File

@@ -6,22 +6,8 @@ using System.Xml;
namespace StarterNG.Services;
/// <summary>
/// Loads UI translations from plain XML files shipped in a <c>lang/</c> folder next
/// to the executable (instead of dictionaries compiled into the assembly). The files
/// are parsed with <see cref="XmlReader"/>, which works under Native AOT — unlike the
/// Avalonia XAML loader, which has no runtime parser in AOT builds.
///
/// File format (lang/en.xml):
/// <code>
/// &lt;Language code="en" name="English"&gt;
/// &lt;String key="NavScenarios"&gt;Scenarios&lt;/String&gt;
/// &lt;/Language&gt;
/// </code>
/// </summary>
public class LocalizationService : INotifyPropertyChanged
{
/// <summary>Folder holding the language files, resolved as starter/lang next to the executable.</summary>
public static string LangDirectory =>
Path.Combine(AppContext.BaseDirectory, "startercfg", "lang");
@@ -29,32 +15,17 @@ public class LocalizationService : INotifyPropertyChanged
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// Raised after a language file is loaded. XAML bindings refresh themselves
/// through <see cref="PropertyChanged"/>; this is for captions that code
/// builds once and has to build again.
/// </summary>
public event Action? LanguageChanged;
/// <summary>Display name of the active language (e.g. "English", "Polski").</summary>
public string CurrentLanguage { get; private set; } = "English";
/// <summary>Short ISO code of the active language (e.g. "en", "pl").</summary>
public string CurrentLangCode { get; private set; } = "en";
/// <summary>Indexer used by the XAML bindings: returns the key itself when missing.</summary>
public string this[string key] =>
_strings.TryGetValue(key, out var value) ? value : key;
/// <summary>
/// Metadata for one discovered language file.
/// </summary>
public readonly record struct LanguageInfo(string Code, string Name, string Path);
/// <summary>
/// Scans the lang/ folder and returns the header metadata of every *.xml file
/// without loading all of its strings. Used to populate the language selector.
/// </summary>
public static IReadOnlyList<LanguageInfo> AvailableLanguages()
{
var list = new List<LanguageInfo>();
@@ -71,17 +42,12 @@ public class LocalizationService : INotifyPropertyChanged
}
catch
{
// Skip malformed files rather than crashing the launcher.
}
}
return list;
}
/// <summary>
/// Loads the language whose code or display name matches <paramref name="codeOrName"/>.
/// Falls back to English ("en") when no match is found. Returns the resolved
/// display name so callers can keep their stored setting consistent.
/// </summary>
public string Load(string codeOrName)
{
var languages = AvailableLanguages();
@@ -97,7 +63,6 @@ public class LocalizationService : INotifyPropertyChanged
}
}
// Fall back to English, then to whatever is available first.
if (match is null)
{
foreach (var lang in languages)
@@ -114,14 +79,12 @@ public class LocalizationService : INotifyPropertyChanged
LoadFile(info.Path, info.Code, info.Name);
else
{
// No files at all: keep keys visible instead of blank UI.
_strings = new Dictionary<string, string>();
CurrentLangCode = "en";
CurrentLanguage = "English";
}
// "Item[]" is the indexer name bindings listen for; the empty name asks
// every binding on this object to re-read.
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Item[]"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty));
LanguageChanged?.Invoke();
@@ -135,16 +98,12 @@ public class LocalizationService : INotifyPropertyChanged
var settings = new XmlReaderSettings { IgnoreComments = true, IgnoreWhitespace = true };
using var reader = XmlReader.Create(path, settings);
// NOTE: ReadElementContentAsString() already advances the reader past the
// element's end tag onto the *next* node, so we must NOT call Read() again
// in that case - doing so would skip every second <String> entry. We only
// advance explicitly for nodes we don't consume here.
while (!reader.EOF)
{
if (reader.NodeType == XmlNodeType.Element && reader.Name == "String")
{
var key = reader.GetAttribute("key");
var value = reader.ReadElementContentAsString(); // advances the reader
var value = reader.ReadElementContentAsString();
if (!string.IsNullOrEmpty(key))
dict[key] = value;
}