diff --git a/I18N Commander/.idea/.idea.I18N Commander/.idea/riderMarkupCache.xml b/I18N Commander/.idea/.idea.I18N Commander/.idea/riderMarkupCache.xml
new file mode 100644
index 0000000..4d68344
--- /dev/null
+++ b/I18N Commander/.idea/.idea.I18N Commander/.idea/riderMarkupCache.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/I18N Commander/.idea/.idea.I18N Commander/.idea/vcs.xml b/I18N Commander/.idea/.idea.I18N Commander/.idea/vcs.xml
index 2e3f692..fa8c83e 100644
--- a/I18N Commander/.idea/.idea.I18N Commander/.idea/vcs.xml
+++ b/I18N Commander/.idea/.idea.I18N Commander/.idea/vcs.xml
@@ -1,5 +1,12 @@
+
+
+
+
+
+
+
diff --git a/I18N Commander/DataModel/Database/SettingGeneratorMode.cs b/I18N Commander/DataModel/Database/SettingGeneratorMode.cs
new file mode 100644
index 0000000..bcfab42
--- /dev/null
+++ b/I18N Commander/DataModel/Database/SettingGeneratorMode.cs
@@ -0,0 +1,7 @@
+namespace DataModel.Database;
+
+public enum SettingGeneratorMode
+{
+ AUTOMATIC,
+ MANUAL,
+}
\ No newline at end of file
diff --git a/I18N Commander/DataModel/Database/SettingNames.cs b/I18N Commander/DataModel/Database/SettingNames.cs
index d4ee3fb..7d419cf 100644
--- a/I18N Commander/DataModel/Database/SettingNames.cs
+++ b/I18N Commander/DataModel/Database/SettingNames.cs
@@ -2,9 +2,16 @@
public static class SettingNames
{
- public static readonly string DEEPL_SOURCE_CULTURE = "DeepL Source Culture";
public static readonly string CULTURE = "Culture";
public static readonly string DEEPL_ACTION = "DeepL Action";
public static readonly string DEEPL_API_KEY = "DeepL API Key";
+ public static readonly string DEEPL_SOURCE_CULTURE = "DeepL Source Culture";
public static readonly string DEEPL_MODE = "DeepL Mode";
+ public static readonly string GENERATOR_MODE = "Generator Mode";
+ public static readonly string GENERATOR_DOTNET_ENABLED = "Generator .NET Enabled";
+ public static readonly string GENERATOR_DOTNET_DESTINATION_PATH = "Generator .NET Destination Path";
+ public static readonly string GENERATOR_DOTNET_NAMESPACE = "Generator .NET Namespace";
+ public static readonly string GENERATOR_DOTNET_DEFAULT_CULTURE = "Generator .NET Default Culture";
+ public static readonly string GENERATOR_GODOT_ENABLED = "Generator Godot Enabled";
+ public static readonly string GENERATOR_GODOT_DESTINATION_PATH = "Generator Godot Destination Path";
}
\ No newline at end of file
diff --git a/I18N Commander/Processor/AppSettings.cs b/I18N Commander/Processor/AppSettings.cs
index 86d2a2e..fc3e43f 100644
--- a/I18N Commander/Processor/AppSettings.cs
+++ b/I18N Commander/Processor/AppSettings.cs
@@ -7,284 +7,200 @@ namespace Processor;
public static class AppSettings
{
+ #region Common DB Code
+
+ private static readonly Dictionary CACHES = new();
+ private static readonly Dictionary CACHE_LOADED = new();
+
+ private static async Task GetSetting(string settingName, T defaultValue)
+ {
+ // When possible, use the cache:
+ if (CACHE_LOADED.ContainsKey(settingName) && CACHE_LOADED[settingName])
+ return (T)CACHES[settingName];
+
+ var settingValue = defaultValue;
+ try
+ {
+ // Get the database:
+ await using var db = ProcessorMeta.ServiceProvider.GetRequiredService();
+
+ // Check, if the setting is already set:
+ if (await db.Settings.FirstOrDefaultAsync(n => n.Code == settingName) is { } existingSetting)
+ {
+ settingValue = settingValue switch
+ {
+ bool => (T)(object)existingSetting.BoolValue,
+ string => (T)(object)existingSetting.TextValue,
+ int => (T)(object)existingSetting.IntegerValue,
+ Guid => (T)(object)existingSetting.GuidValue,
+
+ SettingDeepLMode or
+ SettingDeepLAction or
+ SettingGeneratorMode => (T)(object)existingSetting.IntegerValue,
+
+ _ => defaultValue,
+ };
+ return settingValue;
+ }
+
+ // Does not exist, so create it:
+ var setting = new Setting
+ {
+ Code = settingName,
+ };
+
+ switch (settingValue)
+ {
+ case bool:
+ setting.BoolValue = (bool)(object)settingValue;
+ break;
+
+ case string:
+ setting.TextValue = (string)(object)settingValue;
+ break;
+
+ case int:
+ setting.IntegerValue = (int)(object)settingValue;
+ break;
+
+ case Guid:
+ setting.GuidValue = (Guid)(object)settingValue;
+ break;
+
+ case SettingDeepLMode:
+ case SettingDeepLAction:
+ case SettingGeneratorMode:
+ setting.IntegerValue = (int)(object)settingValue;
+ break;
+ }
+
+ await db.Settings.AddAsync(setting);
+ await db.SaveChangesAsync();
+ return settingValue;
+ }
+ finally
+ {
+ CACHE_LOADED[settingName] = true;
+ CACHES[settingName] = settingValue!;
+ }
+ }
+
+ public static async Task SetSetting(string settingName, T settingValue)
+ {
+ // Update the cache:
+ CACHES[settingName] = settingValue!;
+ CACHE_LOADED[settingName] = true;
+
+ // Get the database:
+ await using var db = ProcessorMeta.ServiceProvider.GetRequiredService();
+
+ // Check, if the setting is already set:
+ if (await db.Settings.FirstOrDefaultAsync(n => n.Code == settingName) is { } existingSetting)
+ {
+ switch (settingValue)
+ {
+ case bool:
+ existingSetting.BoolValue = (bool)(object)settingValue;
+ break;
+
+ case string:
+ existingSetting.TextValue = (string)(object)settingValue;
+ break;
+
+ case int:
+ existingSetting.IntegerValue = (int)(object)settingValue;
+ break;
+
+ case Guid:
+ existingSetting.GuidValue = (Guid)(object)settingValue;
+ break;
+
+ case SettingDeepLMode:
+ case SettingDeepLAction:
+ case SettingGeneratorMode:
+ existingSetting.IntegerValue = (int)(object)settingValue;
+ break;
+ }
+ await db.SaveChangesAsync();
+ }
+
+ // Does not exist, so create it:
+ else
+ {
+ var setting = new Setting
+ {
+ Code = SettingNames.GENERATOR_DOTNET_ENABLED,
+ };
+
+ switch (settingValue)
+ {
+ case bool:
+ setting.BoolValue = (bool)(object)settingValue;
+ break;
+
+ case string:
+ setting.TextValue = (string)(object)settingValue;
+ break;
+
+ case int:
+ setting.IntegerValue = (int)(object)settingValue;
+ break;
+
+ case Guid:
+ setting.GuidValue = (Guid)(object)settingValue;
+ break;
+
+ case SettingDeepLMode:
+ case SettingDeepLAction:
+ case SettingGeneratorMode:
+ setting.IntegerValue = (int)(object)settingValue;
+ break;
+ }
+
+ await db.Settings.AddAsync(setting);
+ await db.SaveChangesAsync();
+ }
+ }
+
+ #endregion
+
#region DeepL Settings
#region DeepL Mode
- private static SettingDeepLMode CACHE_DEEPL_MODE = SettingDeepLMode.DISABLED;
- private static bool CACHE_DEEPL_MODE_IS_LOADED = false;
-
- public static async Task SetDeepLMode(SettingDeepLMode mode)
- {
- // Convert the enum to its int value:
- var intValue = (int)mode;
-
- // Update the cache:
- CACHE_DEEPL_MODE = mode;
- CACHE_DEEPL_MODE_IS_LOADED = true;
-
- // Get the database:
- await using var db = ProcessorMeta.ServiceProvider.GetRequiredService();
-
- // Check, if the setting is already set:
- if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_MODE) is {} existingSetting)
- {
- existingSetting.IntegerValue = intValue;
- await db.SaveChangesAsync();
- }
-
- // Does not exist, so create it:
- else
- {
- var setting = new Setting
- {
- Code = SettingNames.DEEPL_MODE,
- IntegerValue = intValue,
- };
-
- await db.Settings.AddAsync(setting);
- await db.SaveChangesAsync();
- }
- }
+ public static async Task SetDeepLMode(SettingDeepLMode mode) => await AppSettings.SetSetting(SettingNames.DEEPL_MODE, mode);
- public static async Task GetDeepLMode()
- {
- if (CACHE_DEEPL_MODE_IS_LOADED)
- return CACHE_DEEPL_MODE;
-
- // Get the database:
- await using var db = ProcessorMeta.ServiceProvider.GetRequiredService();
-
- // Check, if the setting is already set:
- if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_MODE) is { } existingSetting)
- return (SettingDeepLMode) existingSetting.IntegerValue;
-
- // Does not exist, so create it:
- var setting = new Setting
- {
- Code = SettingNames.DEEPL_MODE,
- IntegerValue = (int)SettingDeepLMode.DISABLED,
- };
-
- await db.Settings.AddAsync(setting);
- await db.SaveChangesAsync();
-
- var mode = (SettingDeepLMode) setting.IntegerValue;
- CACHE_DEEPL_MODE = mode;
- CACHE_DEEPL_MODE_IS_LOADED = true;
-
- return mode;
- }
+ public static async Task GetDeepLMode() => await AppSettings.GetSetting(SettingNames.DEEPL_MODE, SettingDeepLMode.DISABLED);
#endregion
#region DeepL API Key
-
- private static string CACHE_DEEPL_API_KEY = string.Empty;
- private static bool CACHE_DEEPL_API_KEY_IS_LOADED = false;
-
- public static async Task SetDeepLAPIKey(string apiKey)
- {
- // Update the cache:
- CACHE_DEEPL_API_KEY = apiKey;
- CACHE_DEEPL_API_KEY_IS_LOADED = true;
-
- // Get the database:
- await using var db = ProcessorMeta.ServiceProvider.GetRequiredService();
-
- // Check, if the setting is already set:
- if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_API_KEY) is {} existingSetting)
- {
- existingSetting.TextValue = apiKey;
- await db.SaveChangesAsync();
- }
-
- // Does not exist, so create it:
- else
- {
- var setting = new Setting
- {
- Code = SettingNames.DEEPL_API_KEY,
- TextValue = apiKey,
- };
-
- await db.Settings.AddAsync(setting);
- await db.SaveChangesAsync();
- }
- }
-
- public static async Task GetDeepLAPIKey()
- {
- // Check the cache:
- if (CACHE_DEEPL_API_KEY_IS_LOADED)
- return CACHE_DEEPL_API_KEY;
-
- // Get the database:
- await using var db = ProcessorMeta.ServiceProvider.GetRequiredService();
-
- // Check, if the setting is already set:
- if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_API_KEY) is { } existingSetting)
- return existingSetting.TextValue;
-
- // Does not exist, so create it:
- var setting = new Setting
- {
- Code = SettingNames.DEEPL_API_KEY,
- TextValue = string.Empty,
- };
-
- await db.Settings.AddAsync(setting);
- await db.SaveChangesAsync();
-
- var key = setting.TextValue;
- CACHE_DEEPL_API_KEY = key;
- CACHE_DEEPL_API_KEY_IS_LOADED = true;
-
- return key;
- }
-
+
+ public static async Task SetDeepLAPIKey(string apiKey) => await AppSettings.SetSetting(SettingNames.DEEPL_API_KEY, apiKey);
+
+ public static async Task GetDeepLAPIKey() => await AppSettings.GetSetting(SettingNames.DEEPL_API_KEY, string.Empty);
+
#endregion
#region DeepL Action
-
- private static SettingDeepLAction CACHE_DEEPL_ACTION = SettingDeepLAction.MANUAL;
- private static bool CACHE_DEEPL_ACTION_IS_LOADED = false;
- public static async Task SetDeepLAction(SettingDeepLAction action)
- {
- // Convert the enum to its int value:
- var intValue = (int)action;
-
- // Update the cache:
- CACHE_DEEPL_ACTION = action;
- CACHE_DEEPL_ACTION_IS_LOADED = true;
-
- // Get the database:
- await using var db = ProcessorMeta.ServiceProvider.GetRequiredService();
-
- // Check, if the setting is already set:
- if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_ACTION) is {} existingSetting)
- {
- existingSetting.IntegerValue = intValue;
- await db.SaveChangesAsync();
- }
-
- // Does not exist, so create it:
- else
- {
- var setting = new Setting
- {
- Code = SettingNames.DEEPL_ACTION,
- IntegerValue = intValue,
- };
-
- await db.Settings.AddAsync(setting);
- await db.SaveChangesAsync();
- }
- }
-
- public static async Task GetDeepLAction()
- {
- // Check the cache:
- if (CACHE_DEEPL_ACTION_IS_LOADED)
- return CACHE_DEEPL_ACTION;
-
- // Get the database:
- await using var db = ProcessorMeta.ServiceProvider.GetRequiredService();
-
- // Check, if the setting is already set:
- if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_ACTION) is { } existingSetting)
- return (SettingDeepLAction) existingSetting.IntegerValue;
-
- // Does not exist, so create it:
- var setting = new Setting
- {
- Code = SettingNames.DEEPL_ACTION,
- IntegerValue = (int)SettingDeepLAction.MANUAL,
- };
-
- await db.Settings.AddAsync(setting);
- await db.SaveChangesAsync();
-
- var action = (SettingDeepLAction) setting.IntegerValue;
- CACHE_DEEPL_ACTION = action;
- CACHE_DEEPL_ACTION_IS_LOADED = true;
-
- return action;
- }
-
+ public static async Task SetDeepLAction(SettingDeepLAction action) => await AppSettings.SetSetting(SettingNames.DEEPL_ACTION, action);
+
+ public static async Task GetDeepLAction() => await AppSettings.GetSetting(SettingNames.DEEPL_ACTION, SettingDeepLAction.MANUAL);
+
#endregion
#region DeepL Source Culture
- private static int CACHE_DEEPL_SOURCE_CULTURE = -1;
- private static bool CACHE_DEEPL_SOURCE_CULTURE_IS_LOADED = false;
-
- public static async Task SetDeepLSourceCultureIndex(int cultureIndex)
- {
- // Update the cache:
- CACHE_DEEPL_SOURCE_CULTURE = cultureIndex;
- CACHE_DEEPL_SOURCE_CULTURE_IS_LOADED = true;
-
- // Get the database:
- await using var db = ProcessorMeta.ServiceProvider.GetRequiredService();
-
- // Check, if the setting is already set:
- if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_SOURCE_CULTURE) is {} existingSetting)
- {
- existingSetting.IntegerValue = cultureIndex;
- await db.SaveChangesAsync();
- }
-
- // Does not exist, so create it:
- else
- {
- var setting = new Setting
- {
- Code = SettingNames.DEEPL_SOURCE_CULTURE,
- IntegerValue = cultureIndex,
- };
-
- await db.Settings.AddAsync(setting);
- await db.SaveChangesAsync();
- }
- }
-
- public static async Task GetDeepLSourceCultureIndex()
- {
- // Check the cache:
- if (CACHE_DEEPL_SOURCE_CULTURE_IS_LOADED)
- return CACHE_DEEPL_SOURCE_CULTURE;
-
- // Get the database:
- await using var db = ProcessorMeta.ServiceProvider.GetRequiredService();
-
- // Check, if the setting is already set:
- if (await db.Settings.FirstOrDefaultAsync(n => n.Code == SettingNames.DEEPL_SOURCE_CULTURE) is { } existingSetting)
- return existingSetting.IntegerValue;
-
- // Does not exist, so create it:
- var setting = new Setting
- {
- Code = SettingNames.DEEPL_SOURCE_CULTURE,
- IntegerValue = -1,
- };
-
- await db.Settings.AddAsync(setting);
- await db.SaveChangesAsync();
-
- var cultureIndex = setting.IntegerValue;
- CACHE_DEEPL_SOURCE_CULTURE = cultureIndex;
- CACHE_DEEPL_SOURCE_CULTURE_IS_LOADED = true;
-
- return cultureIndex;
- }
-
+ public static async Task SetDeepLSourceCultureIndex(int cultureIndex) => await AppSettings.SetSetting(SettingNames.DEEPL_SOURCE_CULTURE, cultureIndex);
+
+ public static async Task GetDeepLSourceCultureIndex() => await AppSettings.GetSetting(SettingNames.DEEPL_SOURCE_CULTURE, -1);
+
#endregion
#endregion
- #region Translation Settings
+ #region Culture Settings
#region List of culture indices
@@ -432,4 +348,64 @@ public static class AppSettings
#endregion
#endregion
+
+ #region Generator Settings
+
+ #region Generator Mode
+
+ public static async Task GetGeneratorMode() => await AppSettings.GetSetting(SettingNames.GENERATOR_MODE, SettingGeneratorMode.MANUAL);
+
+ public static async Task SetGeneratorMode(SettingGeneratorMode mode) => await AppSettings.SetSetting(SettingNames.GENERATOR_MODE, mode);
+
+ #endregion
+
+ #region .NET Generator Enabled/Disabled
+
+ public static async Task GetGeneratorDotnetEnabled() => await AppSettings.GetSetting(SettingNames.GENERATOR_DOTNET_ENABLED, false);
+
+ public static async Task SetGeneratorDotnetEnabled(bool enabled) => await AppSettings.SetSetting(SettingNames.GENERATOR_DOTNET_ENABLED, enabled);
+
+ #endregion
+
+ #region .NET Generator Destination Path
+
+ public static async Task GetGeneratorDotnetDestinationPath() => await AppSettings.GetSetting(SettingNames.GENERATOR_DOTNET_DESTINATION_PATH, string.Empty);
+
+ public static async Task SetGeneratorDotnetDestinationPath(string path) => await AppSettings.SetSetting(SettingNames.GENERATOR_DOTNET_DESTINATION_PATH, path);
+
+ #endregion
+
+ #region .NET Generator Namespace
+
+ public static async Task GetGeneratorDotnetNamespace() => await AppSettings.GetSetting(SettingNames.GENERATOR_DOTNET_NAMESPACE, "I18N");
+
+ public static async Task SetGeneratorDotnetNamespace(string updatedNamespace) => await AppSettings.SetSetting(SettingNames.GENERATOR_DOTNET_NAMESPACE, updatedNamespace);
+
+ #endregion
+
+ #region .NET Generator Default Culture
+
+ public static async Task GetGeneratorDotnetDefaultCultureIndex() => await AppSettings.GetSetting(SettingNames.GENERATOR_DOTNET_DEFAULT_CULTURE, 0);
+
+ public static async Task SetGeneratorDotnetDefaultCultureIndex(int updatedCulture) => await AppSettings.SetSetting(SettingNames.GENERATOR_DOTNET_DEFAULT_CULTURE, updatedCulture);
+
+ #endregion
+
+ #region Godot Generator Enabled/Disabled
+
+ public static async Task GetGeneratorGodotEnabled() => await AppSettings.GetSetting(SettingNames.GENERATOR_GODOT_ENABLED, false);
+
+ public static async Task SetGeneratorGodotEnabled(bool enabled) => await AppSettings.SetSetting(SettingNames.GENERATOR_GODOT_ENABLED, enabled);
+
+ #endregion
+
+ #region Godot Generator Destination Path
+
+ public static async Task GetGeneratorGodotDestinationPath() => await AppSettings.GetSetting(SettingNames.GENERATOR_GODOT_DESTINATION_PATH, string.Empty);
+
+ public static async Task SetGeneratorGodotDestinationPath(string path) => await AppSettings.SetSetting(SettingNames.GENERATOR_GODOT_DESTINATION_PATH, path);
+
+ #endregion
+
+ #endregion
}
\ No newline at end of file
diff --git a/I18N Commander/Processor/DeepL.cs b/I18N Commander/Processor/DeepL.cs
index 3e7d8fd..af3ffe5 100644
--- a/I18N Commander/Processor/DeepL.cs
+++ b/I18N Commander/Processor/DeepL.cs
@@ -28,7 +28,7 @@ public static class DeepL
return new DeepLUsage(true, usage.Character!.Count, usage.Character.Limit);
}
- catch (AuthorizationException e)
+ catch (AuthorizationException)
{
DEEPL_NOT_AVAILABLE = true;
return new DeepLUsage(false, 0, 1, true);
@@ -63,7 +63,7 @@ public static class DeepL
return translation.Text;
}
- catch (AuthorizationException e)
+ catch (AuthorizationException)
{
DEEPL_NOT_AVAILABLE = true;
return string.Empty;
diff --git a/I18N Commander/Processor/Generators/DotnetBigFile.cs b/I18N Commander/Processor/Generators/DotnetBigFile.cs
new file mode 100644
index 0000000..7b20abd
--- /dev/null
+++ b/I18N Commander/Processor/Generators/DotnetBigFile.cs
@@ -0,0 +1,175 @@
+using System.Text;
+using DataModel.Database;
+
+namespace Processor.Generators;
+
+public class DotnetBigFile : IGenerator
+{
+ private static readonly List CULTURE_CODES = new();
+ private static int DEFAULT_CULTURE_INDEX = -1;
+
+ public async Task> 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(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($"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};");
+ await writer.WriteLineAsync();
+ await writer.WriteLineAsync($"{indentionString}private static int PREVIOUS_CULTURE = -1;");
+
+ // 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(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(destBytesWritten, true, string.Empty);
+ else
+ return new ProcessorResult(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 indentionPropString = this.AddIndention(indention + 1);
+ var indentionPropInner1String = this.AddIndention(indention + 2);
+ var indentionPropInner2String = this.AddIndention(indention + 3);
+ var indentionPropInner3String = this.AddIndention(indention + 4);
+
+ await writer.WriteLineAsync($"{indentionString}private static string E_{textElement.Code}_CACHE = \"\";");
+ await writer.WriteLineAsync($"{indentionString}public static string E_{textElement.Code}");
+ await writer.WriteLineAsync($"{indentionString}{{");
+ await writer.WriteLineAsync($"{indentionPropString}get");
+ await writer.WriteLineAsync($"{indentionPropString}{{");
+ await writer.WriteLineAsync($"{indentionPropInner1String}var currentCulture = CultureInfo.CurrentCulture.Name;");
+ await writer.WriteLineAsync($"{indentionPropInner1String}if(PREVIOUS_CULTURE == currentCulture.GetHashCode())");
+ await writer.WriteLineAsync($"{indentionPropInner2String}return E_{textElement.Code}_CACHE;");
+ await writer.WriteLineAsync($"{indentionPropInner1String}else");
+ await writer.WriteLineAsync($"{indentionPropInner1String}{{");
+ await writer.WriteLineAsync($"{indentionPropInner2String}PREVIOUS_CULTURE = currentCulture.GetHashCode();");
+ for (var cultureIndex = 0; cultureIndex < CULTURE_CODES.Count; cultureIndex++)
+ {
+ if(cultureIndex == 0)
+ await writer.WriteLineAsync($"{indentionPropInner2String}if (currentCulture.StartsWith(\"{CULTURE_CODES[cultureIndex]}\", StringComparison.InvariantCultureIgnoreCase))");
+ else
+ await writer.WriteLineAsync($"{indentionPropInner2String}else if (currentCulture.StartsWith(\"{CULTURE_CODES[cultureIndex]}\", StringComparison.InvariantCultureIgnoreCase))");
+
+ await writer.WriteLineAsync($"{indentionPropInner2String}{{");
+ var cultureTranslation = textElement.Translations.FirstOrDefault(x => x.Culture == CULTURE_CODES[cultureIndex]);
+ var cultureText = cultureTranslation?.Text ?? string.Empty;
+ await writer.WriteLineAsync($"{indentionPropInner3String}var text = @\"{Utils.MadeVerbatimStringLiteral(cultureText)}\";");
+ await writer.WriteLineAsync($"{indentionPropInner3String}E_{textElement.Code}_CACHE = text;");
+ await writer.WriteLineAsync($"{indentionPropInner3String}return text;");
+ await writer.WriteLineAsync($"{indentionPropInner2String}}}");
+ }
+
+ // Add the default case:
+ await writer.WriteLineAsync($"{indentionPropInner2String}else");
+ await writer.WriteLineAsync($"{indentionPropInner2String}{{");
+ var defaultCultureTranslation = textElement.Translations.FirstOrDefault(x => x.Culture == CULTURE_CODES[DEFAULT_CULTURE_INDEX]);
+ var defaultCultureText = defaultCultureTranslation?.Text ?? string.Empty;
+ await writer.WriteLineAsync($"{indentionPropInner3String}var text = @\"{Utils.MadeVerbatimStringLiteral(defaultCultureText)}\";");
+ await writer.WriteLineAsync($"{indentionPropInner3String}E_{textElement.Code}_CACHE = text;");
+ await writer.WriteLineAsync($"{indentionPropInner3String}return text;");
+ await writer.WriteLineAsync($"{indentionPropInner2String}}}");
+
+ await writer.WriteLineAsync($"{indentionPropInner1String}}}");
+ await writer.WriteLineAsync($"{indentionPropString}}}");
+ await writer.WriteLineAsync($"{indentionString}}}");
+ await writer.WriteLineAsync();
+ }
+
+ private async Task CreateStaticClass(TextWriter writer, string name, int indention, Func 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}}}");
+ }
+}
\ No newline at end of file
diff --git a/I18N Commander/Processor/Generators/Generator.cs b/I18N Commander/Processor/Generators/Generator.cs
new file mode 100644
index 0000000..29ed588
--- /dev/null
+++ b/I18N Commander/Processor/Generators/Generator.cs
@@ -0,0 +1,41 @@
+namespace Processor.Generators;
+
+public static class Generator
+{
+ private static readonly Dictionary GENERATORS = new();
+ private static readonly IGenerator VOID_GENERATOR = new VoidGenerator();
+
+ public static IGenerator Get(Type genType) => genType switch
+ {
+ Type.DOTNET => GENERATORS.ContainsKey(genType) ? GENERATORS[genType] : GENERATORS[genType] = new DotnetBigFile(),
+
+ _ => VOID_GENERATOR,
+ };
+
+ public static async Task> TriggerAllAsync()
+ {
+ var dotnetEnabled = await AppSettings.GetGeneratorDotnetEnabled();
+ var godotEnabled = await AppSettings.GetGeneratorGodotEnabled();
+ long bytesWritten = 0;
+
+ if (dotnetEnabled)
+ {
+ var result = await Generator.Get(Type.DOTNET).GenerateAsync();
+ if(!result.Successful)
+ return result;
+
+ bytesWritten += result.Result;
+ }
+
+ if(godotEnabled)
+ {
+ var result = await Generator.Get(Type.GODOT).GenerateAsync();
+ if(!result.Successful)
+ return result;
+
+ bytesWritten += result.Result;
+ }
+
+ return new ProcessorResult(bytesWritten);
+ }
+}
\ No newline at end of file
diff --git a/I18N Commander/Processor/Generators/IGenerator.cs b/I18N Commander/Processor/Generators/IGenerator.cs
new file mode 100644
index 0000000..39ded9e
--- /dev/null
+++ b/I18N Commander/Processor/Generators/IGenerator.cs
@@ -0,0 +1,6 @@
+namespace Processor.Generators;
+
+public interface IGenerator
+{
+ public Task> GenerateAsync();
+}
\ No newline at end of file
diff --git a/I18N Commander/Processor/Generators/Type.cs b/I18N Commander/Processor/Generators/Type.cs
new file mode 100644
index 0000000..dc1ef79
--- /dev/null
+++ b/I18N Commander/Processor/Generators/Type.cs
@@ -0,0 +1,9 @@
+namespace Processor.Generators;
+
+public enum Type
+{
+ NONE,
+
+ DOTNET,
+ GODOT,
+}
\ No newline at end of file
diff --git a/I18N Commander/Processor/Generators/VoidGenerator.cs b/I18N Commander/Processor/Generators/VoidGenerator.cs
new file mode 100644
index 0000000..c3334fe
--- /dev/null
+++ b/I18N Commander/Processor/Generators/VoidGenerator.cs
@@ -0,0 +1,6 @@
+namespace Processor.Generators;
+
+public class VoidGenerator : IGenerator
+{
+ public Task GenerateAsync() => Task.CompletedTask;
+}
\ No newline at end of file
diff --git a/I18N Commander/Processor/SectionProcessor.cs b/I18N Commander/Processor/SectionProcessor.cs
index f66275b..614fa48 100644
--- a/I18N Commander/Processor/SectionProcessor.cs
+++ b/I18N Commander/Processor/SectionProcessor.cs
@@ -198,4 +198,12 @@ public static class SectionProcessor
return $"Section's path: {path}";
}
+
+ public static async Task> GetChildSections(string sectionKey)
+ {
+ await using var db = ProcessorMeta.ServiceProvider.GetRequiredService();
+ var section = await db.Sections.FirstAsync(n => n.DataKey == sectionKey);
+
+ return await db.Sections.Where(n => n.Parent == section).ToListAsync();
+ }
}
\ No newline at end of file
diff --git a/I18N Commander/Processor/TextElementProcessor.cs b/I18N Commander/Processor/TextElementProcessor.cs
index ae71b46..1924cab 100644
--- a/I18N Commander/Processor/TextElementProcessor.cs
+++ b/I18N Commander/Processor/TextElementProcessor.cs
@@ -124,7 +124,7 @@ public static class TextElementProcessor
// Save the changes:
await db.SaveChangesAsync();
}
- catch (DbUpdateException updateException)
+ catch (DbUpdateException)
{
}
}
diff --git a/I18N Commander/Processor/Utils.cs b/I18N Commander/Processor/Utils.cs
index 9c19df2..13a625d 100644
--- a/I18N Commander/Processor/Utils.cs
+++ b/I18N Commander/Processor/Utils.cs
@@ -1,4 +1,5 @@
using System.Linq.Expressions;
+using System.Text;
using Microsoft.EntityFrameworkCore;
namespace Processor;
@@ -46,4 +47,25 @@ internal static class Utils
return code;
}
+
+ public static string MadeVerbatimStringLiteral(string text)
+ {
+ IEnumerable ConvertAll(string source)
+ {
+ foreach(var c in source)
+ if(c == '"')
+ {
+ yield return '"';
+ yield return '"';
+ }
+ else
+ yield return c;
+ }
+
+ var sb = new StringBuilder();
+ foreach(var c in ConvertAll(text))
+ sb.Append(c);
+
+ return sb.ToString();
+ }
}
\ No newline at end of file
diff --git a/I18N Commander/UI WinForms/AppEvents.cs b/I18N Commander/UI WinForms/AppEvents.cs
index 5ba9c0d..1145e8e 100644
--- a/I18N Commander/UI WinForms/AppEvents.cs
+++ b/I18N Commander/UI WinForms/AppEvents.cs
@@ -4,10 +4,26 @@ namespace UI_WinForms;
internal static class AppEvents
{
+ internal static void ResetAllSubscriptions()
+ {
+ WhenSettingsChanged = null;
+ WhenSectionChanged = null;
+ WhenTextElementChanged = null;
+ WhenTranslationChanged = null;
+ }
+
+ #region Event: Settings were
+
+ internal static event EventHandler? WhenSettingsChanged;
+
+ internal static void SettingsChanged() => WhenSettingsChanged?.Invoke(null, EventArgs.Empty);
+
+ #endregion
+
#region Event: Section was changed
// Section changed event which can be subscribed:
- internal static event EventHandler WhenSectionChanged;
+ internal static event EventHandler? WhenSectionChanged;
// Method to raise the section changed event:
internal static void SectionChanged(Section section) => WhenSectionChanged?.Invoke(null, section);
@@ -17,7 +33,7 @@ internal static class AppEvents
#region Event: Text element was changed
// Text element changed event which can be subscribed:
- internal static event EventHandler WhenTextElementChanged;
+ internal static event EventHandler? WhenTextElementChanged;
// Method to raise the text element changed event:
internal static void TextElementChanged(TextElement? textElement) => WhenTextElementChanged?.Invoke(null, textElement);
@@ -27,7 +43,7 @@ internal static class AppEvents
#region Translation was changed
// Translation changed event which can be subscribed:
- internal static event EventHandler WhenTranslationChanged;
+ internal static event EventHandler? WhenTranslationChanged;
// Method to raise the translation changed event:
internal static void TranslationChanged(Translation? translation) => WhenTranslationChanged?.Invoke(null, translation);
diff --git a/I18N Commander/UI WinForms/Components/Main.cs b/I18N Commander/UI WinForms/Components/Main.cs
index af58cec..b4e1950 100644
--- a/I18N Commander/UI WinForms/Components/Main.cs
+++ b/I18N Commander/UI WinForms/Components/Main.cs
@@ -16,6 +16,7 @@ public partial class Main : UserControl
if(result == DialogResult.Yes)
{
Program.RestartMainApp = true;
+ AppEvents.ResetAllSubscriptions();
this.ParentForm!.Close();
}
}
diff --git a/I18N Commander/UI WinForms/Components/SectionTree.Designer.cs b/I18N Commander/UI WinForms/Components/SectionTree.Designer.cs
index ebd2639..219fbad 100644
--- a/I18N Commander/UI WinForms/Components/SectionTree.Designer.cs
+++ b/I18N Commander/UI WinForms/Components/SectionTree.Designer.cs
@@ -34,6 +34,7 @@
this.buttonAdd = new System.Windows.Forms.Button();
this.buttonRemove = new System.Windows.Forms.Button();
this.buttonRename = new System.Windows.Forms.Button();
+ this.buttonGenerate = new System.Windows.Forms.Button();
this.treeView = new System.Windows.Forms.TreeView();
this.toolTip = new System.Windows.Forms.ToolTip(this.components);
this.tableLayout.SuspendLayout();
@@ -61,6 +62,7 @@
this.flowLayoutBottom.Controls.Add(this.buttonAdd);
this.flowLayoutBottom.Controls.Add(this.buttonRemove);
this.flowLayoutBottom.Controls.Add(this.buttonRename);
+ this.flowLayoutBottom.Controls.Add(this.buttonGenerate);
this.flowLayoutBottom.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutBottom.Location = new System.Drawing.Point(0, 445);
this.flowLayoutBottom.Margin = new System.Windows.Forms.Padding(0);
@@ -115,6 +117,22 @@
this.buttonRename.UseVisualStyleBackColor = true;
this.buttonRename.Click += new System.EventHandler(this.buttonRename_Click);
//
+ // buttonGenerate
+ //
+ this.buttonGenerate.AutoSize = true;
+ this.buttonGenerate.Enabled = false;
+ this.buttonGenerate.FlatAppearance.BorderSize = 0;
+ this.buttonGenerate.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+ this.buttonGenerate.Image = global::UI_WinForms.Resources.Icons.icons8_code_512__2_;
+ this.buttonGenerate.Location = new System.Drawing.Point(201, 3);
+ this.buttonGenerate.Name = "buttonGenerate";
+ this.buttonGenerate.Size = new System.Drawing.Size(60, 60);
+ this.buttonGenerate.TabIndex = 3;
+ this.buttonGenerate.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageBeforeText;
+ this.toolTip.SetToolTip(this.buttonGenerate, "Triggers all enabled generators");
+ this.buttonGenerate.UseVisualStyleBackColor = true;
+ this.buttonGenerate.Click += new System.EventHandler(this.buttonGenerate_Click);
+ //
// treeView
//
this.treeView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
@@ -159,5 +177,6 @@
private TreeView treeView;
private ToolTip toolTip;
private Button buttonRename;
+ private Button buttonGenerate;
}
}
diff --git a/I18N Commander/UI WinForms/Components/SectionTree.cs b/I18N Commander/UI WinForms/Components/SectionTree.cs
index d402363..878fe74 100644
--- a/I18N Commander/UI WinForms/Components/SectionTree.cs
+++ b/I18N Commander/UI WinForms/Components/SectionTree.cs
@@ -1,13 +1,16 @@
using DataModel.Database;
using Processor;
+using Processor.Generators;
using UI_WinForms.Dialogs;
using UI_WinForms.Resources;
+using Timer = System.Timers.Timer;
namespace UI_WinForms.Components;
public partial class SectionTree : UserControl
{
- private static readonly Dictionary> NODE_PROGRESS_HANDLERS = new();
+ private static readonly Dictionary?> NODE_PROGRESS_HANDLERS = new();
+ private readonly Timer generatorTimer;
public SectionTree()
{
@@ -26,10 +29,39 @@ public partial class SectionTree : UserControl
// Set the image list to the tree view:
this.treeView.ImageList = imgList;
+ // The generator timer:
+ this.generatorTimer = new Timer
+ {
+ Enabled = false, // disable timer for now,
+ Interval = 6_000, // 6 second interval,
+ AutoReset = false, // runs only once
+ };
+
+ this.generatorTimer.Elapsed += async (sender, args) => await this.PerformGenerators();
+
// Subscribe to the load event:
this.Load += this.LoadNodes;
+ this.Load += (sender, args) => this.SetupGeneratorButton();
+
+ // Subscribe to all event for triggering the generators:
+ AppEvents.WhenTranslationChanged += (sender, translation) => this.GeneratorEvent();
+ AppEvents.WhenSettingsChanged += (sender, args) => this.GeneratorEvent();
}
+ private async void SetupGeneratorButton()
+ {
+ this.buttonGenerate.Enabled = false;
+
+ // Depend the generator button's visibility on the generator settings:
+ this.buttonGenerate.Enabled = await AppSettings.GetGeneratorDotnetEnabled() || await AppSettings.GetGeneratorGodotEnabled();
+
+ // Subscribe to the changed settings event:
+ AppEvents.WhenSettingsChanged += async (sender, args) =>
+ {
+ this.buttonGenerate.Enabled = await AppSettings.GetGeneratorDotnetEnabled() || await AppSettings.GetGeneratorGodotEnabled();
+ };
+ }
+
private async void LoadNodes(object? sender, EventArgs e)
{
if(this.DesignMode)
@@ -277,4 +309,31 @@ public partial class SectionTree : UserControl
selectedNode.Text = alteredSection.Result!.Name;
selectedNode.Name = alteredSection.Result.DataKey; // [sic] name is the key
}
+
+ private async void buttonGenerate_Click(object sender, EventArgs e) => await this.PerformGenerators();
+
+ private async void GeneratorEvent()
+ {
+ if(await AppSettings.GetGeneratorMode() == SettingGeneratorMode.MANUAL)
+ return;
+
+ if (this.generatorTimer.Enabled)
+ this.generatorTimer.Stop();
+ this.generatorTimer.Start();
+ }
+
+ private async Task PerformGenerators()
+ {
+ if (this.buttonGenerate.InvokeRequired)
+ {
+ await this.buttonGenerate.Invoke(this.PerformGenerators);
+ return;
+ }
+
+ this.buttonGenerate.Enabled = false;
+ var result = await Generator.TriggerAllAsync();
+ result.ProcessError();
+
+ this.buttonGenerate.Enabled = true;
+ }
}
\ No newline at end of file
diff --git a/I18N Commander/UI WinForms/Components/Setting.cs b/I18N Commander/UI WinForms/Components/Setting.cs
index 2bee739..0d248b0 100644
--- a/I18N Commander/UI WinForms/Components/Setting.cs
+++ b/I18N Commander/UI WinForms/Components/Setting.cs
@@ -77,6 +77,8 @@ public sealed partial class Setting : UserControl
{
if(this.data.ChangeNeedsRestart)
this.needRestart = true;
+
+ AppEvents.SettingsChanged();
}
private void UpdateExplanation() => this.labelExplanation.Text = this.data.SettingExplanation();
@@ -210,7 +212,7 @@ public sealed partial class Setting : UserControl
};
// Update the explanation text. Therefore, we need to get the setting object through the chain of parents:
- var setting = (Setting) ((Control) sender).Parent.Parent.Parent;
+ var setting = (Setting) (((Control) sender!)!).Parent.Parent.Parent;
setting.UpdateExplanation();
};
@@ -257,7 +259,6 @@ public sealed partial class Setting : UserControl
};
// Setup the change event handler:
- dropdown.SelectedValueChanged += (sender, args) => changeTrigger();
dropdown.SelectedValueChanged += async (sender, args) => await AppSettings.SetDeepLAction(dropdown.SelectedIndex switch
{
0 => SettingDeepLAction.MANUAL,
@@ -265,6 +266,7 @@ public sealed partial class Setting : UserControl
_ => SettingDeepLAction.MANUAL,
});
+ dropdown.SelectedValueChanged += (sender, args) => changeTrigger();
// Apply the desired layout:
dropdown.Dock = DockStyle.Fill;
@@ -314,12 +316,12 @@ public sealed partial class Setting : UserControl
dropdown.SelectedIndex = currentCultureDropdownIndex;
// Setup the change event handler:
- dropdown.SelectedValueChanged += (sender, args) => changeTrigger();
dropdown.SelectedValueChanged += async (sender, args) =>
{
if(dropdown.SelectedItem is ComboBoxItem selectedItem)
await AppSettings.SetDeepLSourceCultureIndex(selectedItem.CultureIndex);
};
+ dropdown.SelectedValueChanged += (sender, args) => changeTrigger();
// Apply the desired layout:
dropdown.Dock = DockStyle.Fill;
@@ -388,14 +390,314 @@ public sealed partial class Setting : UserControl
cultureIndices.Remove(innerLoopIndex);
}
}
+
+ private static async Task ShowGeneratorModeSettingAsync()
+ {
+ var currentSetting = await AppSettings.GetGeneratorMode();
+
+ var settingData = new SettingUIData(
+ Icon: Icons.icons8_code_512,
+ SettingName: () => "Generator Mode",
+ ChangeNeedsRestart: false,
+ SettingExplanation: () => "The generator mode determines how the translation files are generated.",
+ SettingExplanationLink: () => (string.Empty, string.Empty),
+ SetupDataControl: (changeTrigger) =>
+ {
+ // We set up a combo box with the available actions:
+ var dropdown = new ComboBox();
+ dropdown.Items.Add("Automatic generation");
+ dropdown.Items.Add("Manual generation");
+ dropdown.SelectedIndex = currentSetting switch
+ {
+ SettingGeneratorMode.AUTOMATIC => 0,
+ SettingGeneratorMode.MANUAL => 1,
+
+ _ => 0,
+ };
+
+ // Setup the change event handler:
+ dropdown.SelectedValueChanged += async (sender, args) => await AppSettings.SetGeneratorMode(dropdown.SelectedIndex switch
+ {
+ 0 => SettingGeneratorMode.AUTOMATIC,
+ 1 => SettingGeneratorMode.MANUAL,
+
+ _ => SettingGeneratorMode.AUTOMATIC,
+ });
+ dropdown.SelectedValueChanged += (sender, args) => changeTrigger();
+
+ // Apply the desired layout:
+ dropdown.Dock = DockStyle.Fill;
+ dropdown.DropDownStyle = ComboBoxStyle.DropDownList;
+
+ return dropdown;
+ }
+ );
+
+ return new Setting(settingData);
+ }
+
+ private static async Task ShowGeneratorDotnetEnabledSettingAsync()
+ {
+ var currentSetting = await AppSettings.GetGeneratorDotnetEnabled();
+
+ var settingData = new SettingUIData(
+ Icon: Icons.icons8_code_512,
+ SettingName: () => "Generator: .NET",
+ ChangeNeedsRestart: false,
+ SettingExplanation: () => "When enabled, .NET translation files are generated. Requires a .NET 6 or newer project.",
+ SettingExplanationLink: () => (string.Empty, string.Empty),
+ SetupDataControl: (changeTrigger) =>
+ {
+ // Set up an checkbox:
+ var checkbox = new CheckBox();
+ checkbox.Checked = currentSetting;
+ checkbox.CheckedChanged += async (sender, args) => await AppSettings.SetGeneratorDotnetEnabled(checkbox.Checked);
+ checkbox.CheckedChanged += (sender, args) => changeTrigger();
+ checkbox.Text = "Enable .NET Generator";
+
+ // Apply the desired layout:
+ checkbox.Dock = DockStyle.Fill;
+ return checkbox;
+ }
+ );
+
+ return new Setting(settingData);
+ }
+
+ private static async Task ShowGeneratorDotnetDestinationPathSettingAsync()
+ {
+ var currentSetting = await AppSettings.GetGeneratorDotnetDestinationPath();
+
+ var settingData = new SettingUIData(
+ Icon: Icons.icons8_code_512,
+ SettingName: () => "Generator: .NET Destination Path",
+ ChangeNeedsRestart: false,
+ SettingExplanation: () => "The destination path for the .NET translation files. You might use environment variables like %USERPROFILE%.",
+ SettingExplanationLink: () => (string.Empty, string.Empty),
+ SetupDataControl: (changeTrigger) =>
+ {
+ // Set up a horizontal layout:
+ var layout = new TableLayoutPanel();
+ layout.ColumnCount = 2;
+ layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
+ layout.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 66F));
+ layout.RowCount = 1;
+ layout.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
+ layout.Dock = DockStyle.Fill;
+
+ // Set up a textbox:
+ var textbox = new TextBox();
+ textbox.Text = currentSetting;
+ textbox.TextChanged += async (sender, args) => await AppSettings.SetGeneratorDotnetDestinationPath(textbox.Text);
+ textbox.TextChanged += (sender, args) => changeTrigger();
+ textbox.Dock = DockStyle.Fill;
+ textbox.Margin = new Padding(0, 13, 0, 13);
+ layout.Controls.Add(textbox, 0, 0);
+
+ // Set up a button:
+ var button = new Button();
+ button.Text = string.Empty;
+ button.Image = Icons.icons8_folder_tree_512;
+ button.FlatStyle = FlatStyle.Flat;
+ button.FlatAppearance.BorderSize = 0;
+ button.BackColor = Color.Empty;
+ button.UseVisualStyleBackColor = true;
+ button.Size = new Size(60, 60);
+ button.Click += (sender, args) =>
+ {
+ var dialog = new FolderBrowserDialog();
+ dialog.SelectedPath = textbox.Text;
+ dialog.InitialDirectory = textbox.Text;
+ dialog.Description = "Select the destination path for the .NET translation files.";
+ dialog.ShowNewFolderButton = true;
+ if (dialog.ShowDialog() == DialogResult.OK)
+ textbox.Text = dialog.SelectedPath;
+ };
+ button.Dock = DockStyle.Fill;
+ layout.Controls.Add(button, 1, 0);
+
+ return layout;
+ }
+ );
+
+ return new Setting(settingData);
+ }
+
+ private static async Task ShowGeneratorDotnetNamespaceSettingAsync()
+ {
+ var currentSetting = await AppSettings.GetGeneratorDotnetNamespace();
+
+ var settingData = new SettingUIData(
+ Icon: Icons.icons8_code_512,
+ SettingName: () => "Generator: .NET Namespace",
+ ChangeNeedsRestart: false,
+ SettingExplanation: () => "The namespace for the .NET I18N files.",
+ SettingExplanationLink: () => (string.Empty, string.Empty),
+ SetupDataControl: (changeTrigger) =>
+ {
+ // Set up a textbox:
+ var textbox = new TextBox();
+ textbox.Text = currentSetting;
+ textbox.TextChanged += async (sender, args) => await AppSettings.SetGeneratorDotnetNamespace(textbox.Text);
+ textbox.TextChanged += (sender, args) => changeTrigger();
+ textbox.Dock = DockStyle.Fill;
+ textbox.Margin = new Padding(0, 13, 0, 13);
+ return textbox;
+ }
+ );
+
+ return new Setting(settingData);
+ }
+
+ private static async Task ShowGeneratorDotnetDefaultCultureSettingAsync()
+ {
+ var currentSourceCultureIndex = await AppSettings.GetGeneratorDotnetDefaultCultureIndex();
+
+ // We load the corresponding culture for that index. As dropdown items, we show
+ // all other available cultures:
+ var allCultures = await AppSettings.GetCultureInfos();
+
+ // Attention: We have to store the culture's index, because the index is not
+ // continuous and can change when the user adds or removes a culture!
+ var settingData = new SettingUIData(
+ Icon: Icons.icons8_code_512,
+ SettingName: () => "Generator: .NET Default Culture",
+ ChangeNeedsRestart: false,
+ SettingExplanation: () => "The default culture for the .NET, which is used when no culture is specified or available.",
+ SettingExplanationLink: () => (string.Empty, string.Empty),
+ SetupDataControl: (changeTrigger) =>
+ {
+ var dropdown = new ComboBox();
+ var currentCultureDropdownIndex = 0;
+ for (var n = 0; n < allCultures.Count; n++)
+ {
+ var cultureInfo = allCultures[n];
+ if(cultureInfo.Index == currentSourceCultureIndex)
+ currentCultureDropdownIndex = n;
+
+ dropdown.Items.Add(new ComboBoxItem($"{cultureInfo.Index}.: {cultureInfo.Code}", cultureInfo.Index));
+ }
+
+ dropdown.SelectedIndex = currentCultureDropdownIndex;
+
+ // Setup the change event handler:
+ dropdown.SelectedValueChanged += async (sender, args) =>
+ {
+ if(dropdown.SelectedItem is ComboBoxItem selectedItem)
+ await AppSettings.SetGeneratorDotnetDefaultCultureIndex(selectedItem.CultureIndex);
+ };
+ dropdown.SelectedValueChanged += (sender, args) => changeTrigger();
+
+ // Apply the desired layout:
+ dropdown.Dock = DockStyle.Fill;
+ dropdown.DropDownStyle = ComboBoxStyle.DropDownList;
+
+ return dropdown;
+ }
+ );
+
+ return new Setting(settingData);
+ }
+
+ private static async Task ShowGeneratorGodotEnabledSettingAsync()
+ {
+ var currentSetting = await AppSettings.GetGeneratorGodotEnabled();
+
+ var settingData = new SettingUIData(
+ Icon: Icons.icons8_code_512,
+ SettingName: () => "Generator: Godot",
+ ChangeNeedsRestart: false,
+ SettingExplanation: () => "When enabled, Godot translation files are generated. Requires a Godot 3.5 or newer project.",
+ SettingExplanationLink: () => (string.Empty, string.Empty),
+ SetupDataControl: (changeTrigger) =>
+ {
+ // Set up an checkbox:
+ var checkbox = new CheckBox();
+ checkbox.Checked = currentSetting;
+ checkbox.CheckedChanged += async (sender, args) => await AppSettings.SetGeneratorGodotEnabled(checkbox.Checked);
+ checkbox.CheckedChanged += (sender, args) => changeTrigger();
+ checkbox.Text = "Enable Godot Generator";
+
+ // Apply the desired layout:
+ checkbox.Dock = DockStyle.Fill;
+ return checkbox;
+ }
+ );
+
+ return new Setting(settingData);
+ }
+
+ private static async Task ShowGeneratorGodotDestinationPathSettingAsync()
+ {
+ var currentSetting = await AppSettings.GetGeneratorGodotDestinationPath();
+
+ var settingData = new SettingUIData(
+ Icon: Icons.icons8_code_512,
+ SettingName: () => "Generator: Godot Destination Path",
+ ChangeNeedsRestart: false,
+ SettingExplanation: () => "The destination path for the Godot translation files. You might use environment variables like %USERPROFILE%.",
+ SettingExplanationLink: () => (string.Empty, string.Empty),
+ SetupDataControl: (changeTrigger) =>
+ {
+ // Set up a horizontal layout:
+ var layout = new TableLayoutPanel();
+ layout.ColumnCount = 2;
+ layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
+ layout.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 66F));
+ layout.RowCount = 1;
+ layout.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
+ layout.Dock = DockStyle.Fill;
+
+ // Set up a textbox:
+ var textbox = new TextBox();
+ textbox.Text = currentSetting;
+ textbox.TextChanged += async (sender, args) => await AppSettings.SetGeneratorGodotDestinationPath(textbox.Text);
+ textbox.TextChanged += (sender, args) => changeTrigger();
+ textbox.Dock = DockStyle.Fill;
+ textbox.Margin = new Padding(0, 13, 0, 13);
+ layout.Controls.Add(textbox, 0, 0);
+
+ // Set up a button:
+ var button = new Button();
+ button.Text = string.Empty;
+ button.Image = Icons.icons8_folder_tree_512;
+ button.FlatStyle = FlatStyle.Flat;
+ button.FlatAppearance.BorderSize = 0;
+ button.BackColor = Color.Empty;
+ button.UseVisualStyleBackColor = true;
+ button.Size = new Size(60, 60);
+ button.Click += (sender, args) =>
+ {
+ var dialog = new FolderBrowserDialog();
+ dialog.SelectedPath = textbox.Text;
+ dialog.InitialDirectory = textbox.Text;
+ dialog.Description = "Select the destination path for the Godot translation files.";
+ dialog.ShowNewFolderButton = true;
+ if (dialog.ShowDialog() == DialogResult.OK)
+ textbox.Text = dialog.SelectedPath;
+ };
+ button.Dock = DockStyle.Fill;
+ layout.Controls.Add(button, 1, 0);
+
+ return layout;
+ }
+ );
+
+ return new Setting(settingData);
+ }
public static IEnumerable> GetAllSettings()
{
+ yield return ShowGeneratorGodotDestinationPathSettingAsync();
+ yield return ShowGeneratorGodotEnabledSettingAsync();
+ yield return ShowGeneratorDotnetDefaultCultureSettingAsync();
+ yield return ShowGeneratorDotnetNamespaceSettingAsync();
+ yield return ShowGeneratorDotnetDestinationPathSettingAsync();
+ yield return ShowGeneratorDotnetEnabledSettingAsync();
+ yield return ShowGeneratorModeSettingAsync();
yield return ShowDeepLSourceCultureSettingAsync();
foreach (var setting in ShowCultureSettingsAsync())
- {
yield return setting;
- }
yield return ShowDeepLUsageSettingAsync();
yield return ShowDeepLActionSettingAsync();
diff --git a/I18N Commander/UI WinForms/Components/Translation.cs b/I18N Commander/UI WinForms/Components/Translation.cs
index 80cee88..7f664cc 100644
--- a/I18N Commander/UI WinForms/Components/Translation.cs
+++ b/I18N Commander/UI WinForms/Components/Translation.cs
@@ -22,6 +22,9 @@ public sealed partial class Translation : UserControl
{
this.InitializeComponent();
this.Dock = DockStyle.Top;
+
+ this.saveTimer = new Timer();
+ this.translationTimer = new Timer();
}
public Translation(AppSettings.CultureInfo cultureInfo)
@@ -121,7 +124,7 @@ public sealed partial class Translation : UserControl
}
}
- private async void SaveChanges(object? sender, ElapsedEventArgs e)
+ private async void SaveChanges(object? sender, ElapsedEventArgs? e)
{
if (this.currentTranslationId > -1)
{
@@ -135,7 +138,7 @@ public sealed partial class Translation : UserControl
this.isManualOnlyMode = this.checkBoxManual.Checked;
}
- private async void TriggerTranslateNow(object? sender, ElapsedEventArgs e)
+ private async void TriggerTranslateNow(object? sender, ElapsedEventArgs? e)
{
if (this.currentTranslationId < 0 || this.isDeepLSourceCulture == false)
return;
diff --git a/I18N Commander/UI WinForms/ExtensionsError.cs b/I18N Commander/UI WinForms/ExtensionsError.cs
index e21b8ee..f265e66 100644
--- a/I18N Commander/UI WinForms/ExtensionsError.cs
+++ b/I18N Commander/UI WinForms/ExtensionsError.cs
@@ -6,7 +6,8 @@ public static class ExtensionsError
{
public static void ProcessError(this ProcessorResult result)
{
- if (result.Successful) return;
+ if (result.Successful)
+ return;
MessageBox.Show(result.ErrorMessage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
diff --git a/I18N Commander/UI WinForms/Resources/Icons.Designer.cs b/I18N Commander/UI WinForms/Resources/Icons.Designer.cs
index cedf6c1..cf5126b 100644
--- a/I18N Commander/UI WinForms/Resources/Icons.Designer.cs
+++ b/I18N Commander/UI WinForms/Resources/Icons.Designer.cs
@@ -140,6 +140,26 @@ namespace UI_WinForms.Resources {
}
}
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap icons8_code_512 {
+ get {
+ object obj = ResourceManager.GetObject("icons8_code_512", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap icons8_code_512__2_ {
+ get {
+ object obj = ResourceManager.GetObject("icons8_code_512__2_", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
///
/// Looks up a localized resource of type System.Drawing.Bitmap.
///
@@ -180,6 +200,16 @@ namespace UI_WinForms.Resources {
}
}
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap icons8_folder_tree_512 {
+ get {
+ object obj = ResourceManager.GetObject("icons8_folder_tree_512", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
///
/// Looks up a localized resource of type System.Drawing.Bitmap.
///
diff --git a/I18N Commander/UI WinForms/Resources/Icons.resx b/I18N Commander/UI WinForms/Resources/Icons.resx
index 9cfa843..8e6f5c8 100644
--- a/I18N Commander/UI WinForms/Resources/Icons.resx
+++ b/I18N Commander/UI WinForms/Resources/Icons.resx
@@ -142,6 +142,12 @@
icons8-chat-bubble-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ icons8-code-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ icons8-code-512 (2).png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
icons8-collectibles-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
@@ -154,6 +160,9 @@
icons8-document-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ icons8-folder-tree-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
icons8-increase-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
diff --git a/I18N Commander/UI WinForms/Resources/icons8-code-512 (2).png b/I18N Commander/UI WinForms/Resources/icons8-code-512 (2).png
new file mode 100644
index 0000000..298896d
Binary files /dev/null and b/I18N Commander/UI WinForms/Resources/icons8-code-512 (2).png differ
diff --git a/I18N Commander/UI WinForms/Resources/icons8-code-512.png b/I18N Commander/UI WinForms/Resources/icons8-code-512.png
new file mode 100644
index 0000000..00d10eb
Binary files /dev/null and b/I18N Commander/UI WinForms/Resources/icons8-code-512.png differ
diff --git a/I18N Commander/UI WinForms/Resources/icons8-folder-tree-512.png b/I18N Commander/UI WinForms/Resources/icons8-folder-tree-512.png
new file mode 100644
index 0000000..60db4b8
Binary files /dev/null and b/I18N Commander/UI WinForms/Resources/icons8-folder-tree-512.png differ