I18NCommander/I18N Commander/Processor/Generators/DotnetBigFile.cs

206 lines
9.5 KiB
C#
Raw Normal View History

using System.Text;
using DataModel.Database;
namespace Processor.Generators;
public class DotnetBigFile : IGenerator
{
private static readonly List<string> CULTURE_CODES = new();
private static int DEFAULT_CULTURE_INDEX = -1;
public async Task<ProcessorResult<long>> GenerateAsync()
{
const string filename = "I18N.cs";
var destPath = await AppSettings.GetGeneratorDotnetDestinationPath();
destPath = Environment.ExpandEnvironmentVariables(destPath);
long destBytesWritten = 0;
var pathFinal = Path.Join(destPath, filename);
var pathTemp = Path.Join(destPath, filename + ".gen");
var issueFinal = string.Empty;
try
{
if(File.Exists(pathTemp))
File.Delete(pathTemp);
}
catch (IOException e)
{
return new ProcessorResult<long>(0, false, $"Cannot delete the temporary file: '{e.Message}'. Hint: Is the ransomware protection enabled in your Windows system? If so, please make sure that the I18N Commander has write permission.");
}
CULTURE_CODES.Clear();
var cultures = await AppSettings.GetCultureInfos();
foreach (var (code, _) in cultures)
CULTURE_CODES.Add(code);
DEFAULT_CULTURE_INDEX = await AppSettings.GetGeneratorDotnetDefaultCultureIndex();
DEFAULT_CULTURE_INDEX -= 1; // 1-based to 0-based
try
{
await using var fileStream = new FileStream(pathTemp, FileMode.CreateNew, FileAccess.Write, FileShare.None);
await using var writer = new StreamWriter(fileStream, Encoding.UTF8);
await writer.WriteLineAsync("""using System.Globalization;""");
await writer.WriteLineAsync($"""namespace {await AppSettings.GetGeneratorDotnetNamespace()};""");
await this.CreateStaticClass(writer, "I18N", 0, async (streamWriter, indention) =>
{
var indentionString = this.AddIndention(indention);
var buildTime = DateTime.UtcNow;
await writer.WriteLineAsync($"{indentionString}public static readonly string BUILD_TIME = \"{buildTime:yyyy.MM.dd HH:mm:ss}\";");
await writer.WriteLineAsync($"{indentionString}public static readonly long BUILD_TIME_TICKS = {buildTime.Ticks};");
// Go through the first layer of sections:
var sections = await SectionProcessor.LoadLayer(0);
foreach (var section in sections)
await this.TransformSection(writer, indention, section);
});
}
catch (IOException e)
{
// Happens, e.g. when the ransomware protection on Windows is active and
// the I18N commander is not on the exclusion list.
return new ProcessorResult<long>(0, false, $"Cannot write the generator's result file: '{e.Message}'. Hint: Is the ransomware protection enabled in your Windows system? If so, please make sure that the I18N Commander has write permission.");
}
finally
{
if (File.Exists(pathTemp))
{
destBytesWritten = new FileInfo(pathTemp).Length;
if (destBytesWritten > 0)
{
try
{
if (File.Exists(pathFinal))
File.Delete(pathFinal);
File.Move(pathTemp, pathFinal);
}
catch (IOException e)
{
// Happens when the file is still in use by the compiler, the IDE, etc.
// Depends on the timing, this happens sometimes. We ignore it, though.
issueFinal = e.Message;
}
}
}
}
if(string.IsNullOrWhiteSpace(issueFinal))
return new ProcessorResult<long>(destBytesWritten, true, string.Empty);
else
return new ProcessorResult<long>(0, false, $"Cannot move the generator's result file to the destination: '{issueFinal}'. Hint: Is the ransomware protection enabled in your Windows system? If so, please make sure that the I18N Commander has write permission.");
}
private string AddIndention(int indention) => new string(' ', indention * 3);
private async Task TransformSection(TextWriter writer, int indention, Section section)
{
await this.CreateStaticClass(writer, section.DataKey, indention, async (_, innerIndention) =>
{
var textElements = section.TextElements;
foreach (var textElement in textElements)
await this.TransformTextElement(writer, innerIndention, textElement);
var childSections = await SectionProcessor.GetChildSections(section.DataKey);
foreach (var childSection in childSections)
await this.TransformSection(writer, innerIndention, childSection);
});
}
private async Task TransformTextElement(TextWriter writer, int indention, TextElement textElement)
{
var indentionString = this.AddIndention(indention);
var indentionInner1 = this.AddIndention(indention + 1);
var indentionInner2 = this.AddIndention(indention + 2);
await writer.WriteLineAsync($"""{indentionString}public static string E_{textElement.Code}() => E_{textElement.Code}(CultureInfo.CurrentCulture);""");
await writer.WriteLineAsync($"""{indentionString}public static string E_{textElement.Code}(CultureInfo culture)""");
await writer.WriteLineAsync($$"""{{indentionString}}{"""); // opening {
// Write the default culture's text:
var defaultCultureTranslation = textElement.Translations.FirstOrDefault(x => x.Culture == CULTURE_CODES[DEFAULT_CULTURE_INDEX]);
var defaultCultureText = defaultCultureTranslation?.Text ?? string.Empty;
await this.WriteTextContent(writer, indentionInner1, textElement.IsMultiLine, defaultCultureText, variableName: "defaultText");
await writer.WriteLineAsync($"""{indentionInner1}var name = culture.Name;""");
await writer.WriteLineAsync($"""{indentionInner1}if(name.Length < 2)""");
await writer.WriteLineAsync($$"""{{indentionInner1}}{"""); // opening {
await writer.WriteLineAsync($"""{indentionInner2}return defaultText;""");
await writer.WriteLineAsync($$"""{{indentionInner1}}}"""); // closing }
for (var cultureIndex = 0; cultureIndex < CULTURE_CODES.Count; cultureIndex++)
{
await writer.WriteLineAsync($"""{indentionInner1}if ({this.CreateIf(CULTURE_CODES[cultureIndex])})""");
await writer.WriteLineAsync($$"""{{indentionInner1}}{"""); // opening {
if(cultureIndex == DEFAULT_CULTURE_INDEX)
await writer.WriteLineAsync($"""{indentionInner2}return defaultText;""");
else
{
var cultureTranslation = textElement.Translations.FirstOrDefault(x => x.Culture == CULTURE_CODES[cultureIndex]);
var cultureText = cultureTranslation?.Text ?? string.Empty;
await this.WriteTextContent(writer, indentionInner2, textElement.IsMultiLine, cultureText);
await writer.WriteLineAsync($"""{indentionInner2}return text;""");
}
await writer.WriteLineAsync($$"""{{indentionInner1}}}"""); // closing }
}
// Add the default case:
await writer.WriteLineAsync($"""{indentionInner1}return defaultText;""");
await writer.WriteLineAsync($$"""{{indentionString}}}"""); // closing }
await writer.WriteLineAsync();
}
private async Task CreateStaticClass(TextWriter writer, string name, int indention, Func<TextWriter, int, Task> content)
{
var indentionString = this.AddIndention(indention);
await writer.WriteLineAsync(indentionString);
await writer.WriteLineAsync($"{indentionString}public static class {name}");
await writer.WriteLineAsync($"{indentionString}{{");
await content(writer, indention + 1);
await writer.WriteLineAsync($"{indentionString}}}");
}
private async Task WriteTextContent(TextWriter writer, string indention, bool isMultiline, string content, string variableName = "text")
{
if(isMultiline)
{
await writer.WriteLineAsync($"""""""""
{indention}const string {variableName} = """"""""
""""""""");
await writer.WriteLineAsync(content);
await writer.WriteLineAsync($"""""""""
"""""""";
""""""""");
}
else
await writer.WriteLineAsync($"""""""""{indention}const string {variableName} = """"""""{content}"""""""";""""""""");
}
private string CreateIf(string cultureCode)
{
if (string.IsNullOrWhiteSpace(cultureCode))
return "false";
var sb = new StringBuilder();
var len = cultureCode.Length;
if (len > 2)
sb.Append($"""name.Length >= {len} && """);
for (var i = 0; i < cultureCode.Length; i++)
{
sb.Append($"""name[{i}] is '{cultureCode[i]}'""");
// When this is not the last character, we need to add " && ":
if (i < cultureCode.Length - 1)
sb.Append(" && ");
}
return sb.ToString();
}
}