295 lines
14 KiB
C#
295 lines
14 KiB
C#
// Project Makoto
|
|
// Copyright (C) 2023 Fortunevale
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY
|
|
|
|
global using Xorog.Logger;
|
|
global using Xorog.UniversalExtensions;
|
|
global using Xorog.UniversalExtensions.Entities;
|
|
global using static TranslationSourceGenerator.Log;
|
|
global using static Xorog.UniversalExtensions.UniversalExtensions;
|
|
global using Newtonsoft.Json;
|
|
using System.Reflection;
|
|
using Newtonsoft.Json.Linq;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace TranslationSourceGenerator;
|
|
|
|
internal class Program
|
|
{
|
|
public string MakotoSourceOrigin => $@"
|
|
// Project Makoto
|
|
// Copyright (C) 2023 Fortunevale
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY
|
|
|
|
namespace {this.Namespace};
|
|
|
|
#pragma warning disable CS8981
|
|
#pragma warning disable CS8618
|
|
#pragma warning disable IDE1006
|
|
public class Translations : ITranslations
|
|
{{
|
|
public Dictionary<string, int> Progress = new();
|
|
public CommandTranslation[] CommandList {{ get; set; }}
|
|
|
|
|
|
#region AutoGenerated
|
|
// InsertPoint
|
|
#endregion AutoGenerated
|
|
}}
|
|
";
|
|
|
|
public string StringsJson { get; set; }
|
|
|
|
public string TranslationCs { get; set; }
|
|
|
|
public string Namespace { get; set; }
|
|
|
|
static void Main(string[] args)
|
|
=> new Program().Execute(args).GetAwaiter().GetResult();
|
|
|
|
public async Task Execute(string[] args)
|
|
{
|
|
if (!Directory.Exists($"{new FileInfo(Assembly.GetExecutingAssembly().FullName).Directory.FullName}/logs"))
|
|
_ = Directory.CreateDirectory($"{new FileInfo(Assembly.GetExecutingAssembly().FullName).Directory.FullName}/logs");
|
|
|
|
_logger = LoggerClient.StartLogger($"{new FileInfo(Assembly.GetExecutingAssembly().FullName).Directory.FullName}/logs/{DateTime.UtcNow:dd-MM-yyyy_HH-mm-ss}.log", CustomLogLevel.Debug, DateTime.UtcNow.AddDays(-3), false);
|
|
|
|
_ = Task.Run(async () =>
|
|
{
|
|
if (!File.Exists(args.Length > 0 ? args[0] : "e252zru2rjb2rtb23jbrj2bthj2bthjb2jtb4jbtb2jtb4hj"))
|
|
{
|
|
_logger.LogError("Translation json expected as arg0");
|
|
return;
|
|
}
|
|
|
|
if (!File.Exists(args.Length > 1 ? args[1] : "e252zru2rjb2rtb23jbrj2bthj2bthjb2jtb4jbtb2jtb4hj"))
|
|
{
|
|
_logger.LogError("Translation json expected as arg1");
|
|
return;
|
|
}
|
|
|
|
if (args.Length < 2)
|
|
{
|
|
_logger.LogError("Namespace expected as arg2");
|
|
return;
|
|
}
|
|
|
|
this.StringsJson = args[0];
|
|
this.TranslationCs = args[1];
|
|
this.Namespace = args[2];
|
|
|
|
_logger.LogDebug("Project Makoto Translation Source Generator started up.");
|
|
_logger.LogDebug("Strings.json Location: {0}", Path.GetFullPath(this.StringsJson));
|
|
_logger.LogDebug("Translation.cs Location: {0}", Path.GetFullPath(this.TranslationCs));
|
|
DateTime lastModify = new();
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
Dictionary<string, List<string>> addedFields = new();
|
|
|
|
FileInfo fileInfo = new(this.StringsJson);
|
|
|
|
if (lastModify != fileInfo.LastWriteTimeUtc)
|
|
{
|
|
try
|
|
{
|
|
List<string> Warnings = new();
|
|
var Insert = "";
|
|
|
|
_logger.LogDebug("Translation file updated. Updating Translations.cs..");
|
|
|
|
var jsonFile = (JObject)JsonConvert.DeserializeObject(File.ReadAllText(this.StringsJson));
|
|
|
|
string CreateValidValueName(string str)
|
|
=> Regex.Replace(str, @"[^a-zA-Z0-9]", "_");
|
|
|
|
void RecursiveHandle(JObject token, string ParentPath, int depth)
|
|
{
|
|
foreach (var item in token)
|
|
{
|
|
var className = CreateValidValueName($"{item.Key.First().ToString().ToLower()}{item.Key.Remove(0, 1)}");
|
|
|
|
switch (className)
|
|
{
|
|
case "object":
|
|
className = $"@{className}";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
var fieldName = CreateValidValueName(item.Key.FirstLetterToUpper());
|
|
var entryPoint = $"{ParentPath}/{className}";
|
|
var IndexPath = $"// {ParentPath} InsertPoint";
|
|
|
|
var InsertPosition = 0;
|
|
if (!ParentPath.IsNullOrWhiteSpace())
|
|
{
|
|
InsertPosition = Insert.IndexOf(IndexPath) + IndexPath.Length;
|
|
}
|
|
|
|
if (!addedFields.ContainsKey(IndexPath))
|
|
addedFields.Add(IndexPath, new());
|
|
|
|
switch (item.Value.Type)
|
|
{
|
|
case JTokenType.Object:
|
|
{
|
|
var containsLocaleCode = false;
|
|
var localeCodeIsArray = false;
|
|
|
|
List<KeyValuePair<string, JToken?>> tokens = new();
|
|
foreach (var subItem in item.Value.ToObject<JObject>())
|
|
{
|
|
tokens.Add(subItem);
|
|
|
|
if (subItem.Key == "en")
|
|
{
|
|
containsLocaleCode = true;
|
|
localeCodeIsArray = subItem.Value.Type == JTokenType.Array;
|
|
}
|
|
}
|
|
|
|
if (containsLocaleCode)
|
|
{
|
|
if (!localeCodeIsArray)
|
|
{
|
|
var line = $"{new string(' ', depth * 4)}public SingleTranslationKey {CreateValidValueName(item.Key)};";
|
|
|
|
if (addedFields[IndexPath].Contains(line))
|
|
continue;
|
|
|
|
_logger.LogDebug("Found SingleKey '{0}'", item.Key);
|
|
Insert = Insert.Insert(InsertPosition, $"\n{line}");
|
|
|
|
addedFields[IndexPath].Add(line);
|
|
|
|
if (!tokens.Any(x => x.Key == "de"))
|
|
Warnings.Add($"String at path {entryPoint} has no de translation");
|
|
}
|
|
else
|
|
{
|
|
var line = $"{new string(' ', depth * 4)}public MultiTranslationKey {CreateValidValueName(item.Key)};";
|
|
|
|
if (addedFields[IndexPath].Contains(line))
|
|
continue;
|
|
|
|
_logger.LogDebug("Found MultiKey '{0}'", item.Key);
|
|
Insert = Insert.Insert(InsertPosition, $"\n{line}");
|
|
|
|
addedFields[IndexPath].Add(line);
|
|
|
|
if (!tokens.Any(x => x.Key == "de"))
|
|
Warnings.Add($"String at path {entryPoint} has no de translation");
|
|
}
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
_logger.LogDebug("Found Group '{0}'", item.Key);
|
|
|
|
var line = $"\n{new string(' ', depth * 4)}public {className} {fieldName};\n" +
|
|
$"{new string(' ', depth * 4)}public sealed class {className}\n" +
|
|
$"{new string(' ', depth * 4)}{{\n" +
|
|
$"{new string(' ', depth * 4)}// {entryPoint} InsertPoint\n" +
|
|
$"{new string(' ', depth * 4)}}}\n";
|
|
|
|
if (addedFields[IndexPath].Contains(line))
|
|
break;
|
|
|
|
Insert = Insert.Insert(InsertPosition, line);
|
|
addedFields[IndexPath].Add(line);
|
|
}
|
|
|
|
RecursiveHandle(item.Value.ToObject<JObject>(), entryPoint, depth + 1);
|
|
break;
|
|
}
|
|
case JTokenType.Integer:
|
|
{
|
|
_logger.LogDebug("Found Int '{0}'", item.Key);
|
|
|
|
var line = $"\n{new string(' ', depth * 4)}public int {CreateValidValueName(item.Key)};";
|
|
|
|
if (addedFields[IndexPath].Contains(line))
|
|
continue;
|
|
|
|
Insert = Insert.Insert(InsertPosition, line);
|
|
addedFields[IndexPath].Add(line);
|
|
break;
|
|
}
|
|
case JTokenType.Array:
|
|
{
|
|
_logger.LogDebug("Found Array '{0}'", item.Key);
|
|
|
|
var line = $"\n{new string(' ', depth * 4)}public {className}[] {fieldName};\n" +
|
|
$"{new string(' ', depth * 4)}public sealed class {className}\n" +
|
|
$"{new string(' ', depth * 4)}{{\n" +
|
|
$"{new string(' ', depth * 4)}// {entryPoint} InsertPoint\n" +
|
|
$"{new string(' ', depth * 4)}}}\n";
|
|
|
|
if (fieldName == "CommandList" && IndexPath == "// InsertPoint")
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!addedFields[IndexPath].Contains(line))
|
|
{
|
|
Insert = Insert.Insert(InsertPosition, line);
|
|
addedFields[IndexPath].Add(line);
|
|
}
|
|
|
|
foreach (var b in item.Value.ToObject<JArray>())
|
|
RecursiveHandle(b.ToObject<JObject>(), entryPoint, depth + 1);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
RecursiveHandle(jsonFile, "", 1);
|
|
foreach (var b in Warnings)
|
|
{
|
|
_logger.LogWarn(b);
|
|
}
|
|
|
|
_logger.LogWarn("Source Generation finished with {Count} warnings.", Warnings.Count);
|
|
|
|
Insert = string.Join("\n", Insert.Split("\n").Where(x => !x.Contains("InsertPoint")));
|
|
|
|
File.WriteAllText(this.TranslationCs, string.Join("\n", this.MakotoSourceOrigin.Replace("// InsertPoint", Insert).ReplaceLineEndings("\n").Split("\n", StringSplitOptions.RemoveEmptyEntries).Where(x => !x.IsNullOrWhiteSpace())));
|
|
|
|
_logger.LogDebug("Updated Translations.cs.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError("Failed to update Translations.cs.", ex);
|
|
}
|
|
}
|
|
|
|
lastModify = fileInfo.LastWriteTimeUtc;
|
|
|
|
await Task.Delay(1000);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError("Failed to watch file", ex);
|
|
await Task.Delay(10000);
|
|
}
|
|
}
|
|
});
|
|
|
|
await Task.Delay(-1);
|
|
}
|
|
}
|