diff --git a/GetGitDir/GetGitDir.csproj b/GetGitDir/GetGitDir.csproj new file mode 100644 index 0000000..cf3fec2 --- /dev/null +++ b/GetGitDir/GetGitDir.csproj @@ -0,0 +1,18 @@ + + + + Exe + net8.0 + enable + enable + + + + embedded + + + + embedded + + + diff --git a/GetGitDir/GetGitDir.exe b/GetGitDir/GetGitDir.exe new file mode 100644 index 0000000..d15b7d1 Binary files /dev/null and b/GetGitDir/GetGitDir.exe differ diff --git a/GetGitDir/GetGitDir.sln b/GetGitDir/GetGitDir.sln new file mode 100644 index 0000000..20bb073 --- /dev/null +++ b/GetGitDir/GetGitDir.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34511.84 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GetGitDir", "GetGitDir.csproj", "{13C3220F-81E4-46E1-9D45-BFE51D425C7A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {13C3220F-81E4-46E1-9D45-BFE51D425C7A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {13C3220F-81E4-46E1-9D45-BFE51D425C7A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {13C3220F-81E4-46E1-9D45-BFE51D425C7A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {13C3220F-81E4-46E1-9D45-BFE51D425C7A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F57CB7EB-01A8-4A5D-ABDF-F6A655B99AE5} + EndGlobalSection +EndGlobal diff --git a/GetGitDir/Program.cs b/GetGitDir/Program.cs new file mode 100644 index 0000000..1b65f30 --- /dev/null +++ b/GetGitDir/Program.cs @@ -0,0 +1,20 @@ +try +{ + if (!new FileInfo(".git").Exists) + return 1; + + var content = File.ReadAllLines(".git"); + + if (!content.Any(x => x.StartsWith("gitdir: "))) + return 1; + + var gitDirLine = content.First(x => x.StartsWith("gitdir: ")); + + Console.WriteLine(gitDirLine[8..]); + return 0; + +} +catch (Exception) +{ + return 1; +} \ No newline at end of file diff --git a/TranslationSourceGenerator/GlobalSuppressions.cs b/TranslationSourceGenerator/GlobalSuppressions.cs new file mode 100644 index 0000000..ea6a637 --- /dev/null +++ b/TranslationSourceGenerator/GlobalSuppressions.cs @@ -0,0 +1,9 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "", Scope = "member", Target = "~P:TranslationSourceGenerator.Log._logger")] +[assembly: SuppressMessage("GeneratedRegex", "SYSLIB1045:Convert to 'GeneratedRegexAttribute'.", Justification = "")] diff --git a/TranslationSourceGenerator/Log.cs b/TranslationSourceGenerator/Log.cs new file mode 100644 index 0000000..ade0837 --- /dev/null +++ b/TranslationSourceGenerator/Log.cs @@ -0,0 +1,15 @@ +// 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 TranslationSourceGenerator; + +internal class Log +{ + internal static LoggerClient _logger { get; set; } +} diff --git a/TranslationSourceGenerator/Program.cs b/TranslationSourceGenerator/Program.cs new file mode 100644 index 0000000..0cc9cb6 --- /dev/null +++ b/TranslationSourceGenerator/Program.cs @@ -0,0 +1,295 @@ +// 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 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> addedFields = new(); + + FileInfo fileInfo = new(this.StringsJson); + + if (lastModify != fileInfo.LastWriteTimeUtc) + { + try + { + List 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> tokens = new(); + foreach (var subItem in item.Value.ToObject()) + { + 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(), 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()) + RecursiveHandle(b.ToObject(), 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); + } +} diff --git a/TranslationSourceGenerator/Properties/launchSettings.json b/TranslationSourceGenerator/Properties/launchSettings.json new file mode 100644 index 0000000..ce5e3c2 --- /dev/null +++ b/TranslationSourceGenerator/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "TranslationSourceGenerator": { + "commandName": "Project", + "commandLineArgs": "\"C:\\Users\\Xorog\\Documents\\GitHub\\ProjectIchigo\\ProjectMakoto\\Translations\\strings.json\" \"C:\\Users\\Xorog\\Documents\\GitHub\\ProjectIchigo\\ProjectMakoto\\Entities\\Translation\\Translations.cs\"" + } + } +} \ No newline at end of file diff --git a/TranslationSourceGenerator/RunDefault.sh b/TranslationSourceGenerator/RunDefault.sh new file mode 100644 index 0000000..2160252 --- /dev/null +++ b/TranslationSourceGenerator/RunDefault.sh @@ -0,0 +1 @@ +dotnet run -- ../../ProjectMakoto/Translations/strings.json ../../ProjectMakoto/Entities/Translation/Translations.cs ProjectMakoto.Entities \ No newline at end of file diff --git a/TranslationSourceGenerator/TranslationSourceGenerator.csproj b/TranslationSourceGenerator/TranslationSourceGenerator.csproj new file mode 100644 index 0000000..c340171 --- /dev/null +++ b/TranslationSourceGenerator/TranslationSourceGenerator.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + enable + annotations + x64 + + + + + + + + diff --git a/TranslationSourceGenerator/TranslationSourceGenerator.sln b/TranslationSourceGenerator/TranslationSourceGenerator.sln new file mode 100644 index 0000000..4206f54 --- /dev/null +++ b/TranslationSourceGenerator/TranslationSourceGenerator.sln @@ -0,0 +1,44 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34309.116 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TranslationSourceGenerator", "TranslationSourceGenerator.csproj", "{1962E64F-BFA8-4ECA-83C6-FD09959D7E98}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xorog.Logger", "..\..\Dependencies\Xorog.Logger\Xorog.Logger.csproj", "{8A8883E2-C01C-48A5-9CFF-16DB013DA0C8}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xorog.UniversalExtensions", "..\..\Dependencies\Xorog.UniversalExtensions\Xorog.UniversalExtensions.csproj", "{1082AEE5-F298-412D-BE0D-12253D25AF1C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + x64|x64 = x64|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1962E64F-BFA8-4ECA-83C6-FD09959D7E98}.Debug|x64.ActiveCfg = Debug|x64 + {1962E64F-BFA8-4ECA-83C6-FD09959D7E98}.Debug|x64.Build.0 = Debug|x64 + {1962E64F-BFA8-4ECA-83C6-FD09959D7E98}.Release|x64.ActiveCfg = Release|x64 + {1962E64F-BFA8-4ECA-83C6-FD09959D7E98}.Release|x64.Build.0 = Release|x64 + {1962E64F-BFA8-4ECA-83C6-FD09959D7E98}.x64|x64.ActiveCfg = Debug|x64 + {1962E64F-BFA8-4ECA-83C6-FD09959D7E98}.x64|x64.Build.0 = Debug|x64 + {8A8883E2-C01C-48A5-9CFF-16DB013DA0C8}.Debug|x64.ActiveCfg = Debug|x64 + {8A8883E2-C01C-48A5-9CFF-16DB013DA0C8}.Debug|x64.Build.0 = Debug|x64 + {8A8883E2-C01C-48A5-9CFF-16DB013DA0C8}.Release|x64.ActiveCfg = Release|x64 + {8A8883E2-C01C-48A5-9CFF-16DB013DA0C8}.Release|x64.Build.0 = Release|x64 + {8A8883E2-C01C-48A5-9CFF-16DB013DA0C8}.x64|x64.ActiveCfg = x64|x64 + {8A8883E2-C01C-48A5-9CFF-16DB013DA0C8}.x64|x64.Build.0 = x64|x64 + {1082AEE5-F298-412D-BE0D-12253D25AF1C}.Debug|x64.ActiveCfg = Debug|x64 + {1082AEE5-F298-412D-BE0D-12253D25AF1C}.Debug|x64.Build.0 = Debug|x64 + {1082AEE5-F298-412D-BE0D-12253D25AF1C}.Release|x64.ActiveCfg = Release|x64 + {1082AEE5-F298-412D-BE0D-12253D25AF1C}.Release|x64.Build.0 = Release|x64 + {1082AEE5-F298-412D-BE0D-12253D25AF1C}.x64|x64.ActiveCfg = x64|x64 + {1082AEE5-F298-412D-BE0D-12253D25AF1C}.x64|x64.Build.0 = x64|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0B29991B-405D-41AD-8AAA-56C1A701ED99} + EndGlobalSection +EndGlobal