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

139 lines
5.9 KiB
C#

using System.Text;
namespace Processor.Generators;
public class GodotCSV : IGenerator
{
private static readonly List<string> CULTURE_CODES = new();
#region Implementation of IGenerator
/// <inheritdoc />
public async Task<ProcessorResult<long>> GenerateAsync()
{
const string filename = "I18N.csv";
const char delimiter = ',';
var destPath = await AppSettings.GetGeneratorGodotDestinationPath();
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.OrderBy(n => n.Code))
CULTURE_CODES.Add(code);
if (CULTURE_CODES.Count == 0)
return new ProcessorResult<long>(0, true, string.Empty);
try
{
await using var fileStream = new FileStream(pathTemp, FileMode.CreateNew, FileAccess.Write, FileShare.None);
await using var writer = new StreamWriter(fileStream, Encoding.UTF8);
// Write the header:
await writer.WriteLineAsync($"keys{delimiter}{string.Join(delimiter, CULTURE_CODES)}");
//
// Iterate through all sections and text elements:
//
var sectionDepth = await SectionProcessor.GetDepth();
foreach (var sectionLayer in Enumerable.Range(0, sectionDepth + 1))
{
var sections = await SectionProcessor.LoadLayer(sectionLayer);
foreach (var section in sections.OrderBy(n => n.DataKey))
foreach (var textElement in section.TextElements.OrderBy(n => n.Code))
{
// Get the key for this text element:
var key = await TextElementProcessor.GetKey(textElement.Id);
// Write the path to the text element as key:
await writer.WriteAsync($"{key}{delimiter}");
// Load all translations:
var translations = textElement.Translations.DistinctBy(n => n.Culture).ToDictionary(translation => translation.Culture, translation => translation.Text);
// Iterate through all text elements and write the translations out:
for (var cultureIndex = 0; cultureIndex < CULTURE_CODES.Count; cultureIndex++)
{
var cultureCode = CULTURE_CODES[cultureIndex];
var cultureText = translations.TryGetValue(cultureCode, out var text) ? text : string.Empty;
// Write the text with delimiter or only the text in case of the last column:
if (cultureIndex < CULTURE_CODES.Count - 1)
await writer.WriteAsync($@"""{Escape4CSV(cultureText)}""{delimiter}");
else
await writer.WriteLineAsync($@"""{Escape4CSV(cultureText)}""");
}
}
}
}
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 static string Escape4CSV(string text)
{
if (string.IsNullOrWhiteSpace(text))
return string.Empty;
var sb = new StringBuilder(text.Length);
foreach (var c in text)
{
if (c == '"')
sb.Append(@""""""); // quoted double quote, i.e. prints ""
else
sb.Append(c);
}
return sb.ToString();
}
#endregion
}