Merge branch '6-add-code-generator-for-non-win-forms-projects' into 'main'
Resolve "Add code generator for non win forms projects" Closes #6 See merge request open-source/dotnet/i18n-commander!20
This commit is contained in:
		
						commit
						cf3791ed70
					
				| @ -0,0 +1,6 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="RiderMarkupStore"> | ||||||
|  |     <option name="version" value="-2154046885505616848" /> | ||||||
|  |   </component> | ||||||
|  | </project> | ||||||
| @ -1,5 +1,12 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||||
| <project version="4"> | <project version="4"> | ||||||
|  |   <component name="CommitMessageInspectionProfile"> | ||||||
|  |     <profile version="1.0"> | ||||||
|  |       <inspection_tool class="BodyLimit" enabled="true" level="ERROR" enabled_by_default="true" /> | ||||||
|  |       <inspection_tool class="SubjectBodySeparation" enabled="true" level="ERROR" enabled_by_default="true" /> | ||||||
|  |       <inspection_tool class="SubjectLimit" enabled="true" level="ERROR" enabled_by_default="true" /> | ||||||
|  |     </profile> | ||||||
|  |   </component> | ||||||
|   <component name="VcsDirectoryMappings"> |   <component name="VcsDirectoryMappings"> | ||||||
|     <mapping directory="$PROJECT_DIR$/.." vcs="Git" /> |     <mapping directory="$PROJECT_DIR$/.." vcs="Git" /> | ||||||
|   </component> |   </component> | ||||||
|  | |||||||
| @ -0,0 +1,7 @@ | |||||||
|  | namespace DataModel.Database; | ||||||
|  | 
 | ||||||
|  | public enum SettingGeneratorMode | ||||||
|  | { | ||||||
|  |     AUTOMATIC, | ||||||
|  |     MANUAL, | ||||||
|  | } | ||||||
| @ -2,9 +2,16 @@ | |||||||
| 
 | 
 | ||||||
| public static class SettingNames | public static class SettingNames | ||||||
| { | { | ||||||
|     public static readonly string DEEPL_SOURCE_CULTURE = "DeepL Source Culture"; |  | ||||||
|     public static readonly string CULTURE = "Culture"; |     public static readonly string CULTURE = "Culture"; | ||||||
|     public static readonly string DEEPL_ACTION = "DeepL Action"; |     public static readonly string DEEPL_ACTION = "DeepL Action"; | ||||||
|     public static readonly string DEEPL_API_KEY = "DeepL API Key"; |     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 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"; | ||||||
| } | } | ||||||
| @ -7,284 +7,200 @@ namespace Processor; | |||||||
| 
 | 
 | ||||||
| public static class AppSettings | public static class AppSettings | ||||||
| { | { | ||||||
|  |     #region Common DB Code | ||||||
|  | 
 | ||||||
|  |     private static readonly Dictionary<string, object> CACHES = new(); | ||||||
|  |     private static readonly Dictionary<string, bool> CACHE_LOADED = new(); | ||||||
|  | 
 | ||||||
|  |     private static async Task<T> GetSetting<T>(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<DataContext>(); | ||||||
|  | 
 | ||||||
|  |             // 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<T>(string settingName, T settingValue) | ||||||
|  |     { | ||||||
|  |         // Update the cache: | ||||||
|  |         CACHES[settingName] = settingValue!; | ||||||
|  |         CACHE_LOADED[settingName] = true; | ||||||
|  |          | ||||||
|  |         // Get the database: | ||||||
|  |         await using var db = ProcessorMeta.ServiceProvider.GetRequiredService<DataContext>(); | ||||||
|  |          | ||||||
|  |         // 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 Settings | ||||||
| 
 | 
 | ||||||
|     #region DeepL Mode |     #region DeepL Mode | ||||||
| 
 | 
 | ||||||
|     private static SettingDeepLMode CACHE_DEEPL_MODE = SettingDeepLMode.DISABLED; |     public static async Task SetDeepLMode(SettingDeepLMode mode) => await AppSettings.SetSetting(SettingNames.DEEPL_MODE, mode); | ||||||
|     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<DataContext>(); |  | ||||||
|          |  | ||||||
|         // 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<SettingDeepLMode> GetDeepLMode() |     public static async Task<SettingDeepLMode> GetDeepLMode() => await AppSettings.GetSetting(SettingNames.DEEPL_MODE, SettingDeepLMode.DISABLED); | ||||||
|     { |  | ||||||
|         if (CACHE_DEEPL_MODE_IS_LOADED) |  | ||||||
|             return CACHE_DEEPL_MODE; |  | ||||||
|          |  | ||||||
|         // Get the database: |  | ||||||
|         await using var db = ProcessorMeta.ServiceProvider.GetRequiredService<DataContext>(); |  | ||||||
|          |  | ||||||
|         // 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; |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     #endregion |     #endregion | ||||||
|      |      | ||||||
|     #region DeepL API Key |     #region DeepL API Key | ||||||
|      | 
 | ||||||
|     private static string CACHE_DEEPL_API_KEY = string.Empty; |     public static async Task SetDeepLAPIKey(string apiKey) => await AppSettings.SetSetting(SettingNames.DEEPL_API_KEY, apiKey); | ||||||
|     private static bool CACHE_DEEPL_API_KEY_IS_LOADED = false; | 
 | ||||||
|      |     public static async Task<string> GetDeepLAPIKey() => await AppSettings.GetSetting(SettingNames.DEEPL_API_KEY, string.Empty); | ||||||
|     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<DataContext>(); |  | ||||||
|          |  | ||||||
|         // 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<string> 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<DataContext>(); |  | ||||||
|          |  | ||||||
|         // 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; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     #endregion |     #endregion | ||||||
|      |      | ||||||
|     #region DeepL Action |     #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) |     public static async Task SetDeepLAction(SettingDeepLAction action) => await AppSettings.SetSetting(SettingNames.DEEPL_ACTION, action); | ||||||
|     { | 
 | ||||||
|         // Convert the enum to its int value: |     public static async Task<SettingDeepLAction> GetDeepLAction() => await AppSettings.GetSetting(SettingNames.DEEPL_ACTION, SettingDeepLAction.MANUAL); | ||||||
|         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<DataContext>(); |  | ||||||
|          |  | ||||||
|         // 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<SettingDeepLAction> GetDeepLAction() |  | ||||||
|     { |  | ||||||
|         // Check the cache: |  | ||||||
|         if (CACHE_DEEPL_ACTION_IS_LOADED) |  | ||||||
|             return CACHE_DEEPL_ACTION; |  | ||||||
|          |  | ||||||
|         // Get the database: |  | ||||||
|         await using var db = ProcessorMeta.ServiceProvider.GetRequiredService<DataContext>(); |  | ||||||
|          |  | ||||||
|         // 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; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     #endregion |     #endregion | ||||||
| 
 | 
 | ||||||
|     #region DeepL Source Culture |     #region DeepL Source Culture | ||||||
|      |      | ||||||
|     private static int CACHE_DEEPL_SOURCE_CULTURE = -1; |     public static async Task SetDeepLSourceCultureIndex(int cultureIndex) => await AppSettings.SetSetting(SettingNames.DEEPL_SOURCE_CULTURE, cultureIndex); | ||||||
|     private static bool CACHE_DEEPL_SOURCE_CULTURE_IS_LOADED = false; | 
 | ||||||
|      |     public static async Task<int> GetDeepLSourceCultureIndex() => await AppSettings.GetSetting(SettingNames.DEEPL_SOURCE_CULTURE, -1); | ||||||
|     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<DataContext>(); |  | ||||||
|          |  | ||||||
|         // 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<int> 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<DataContext>(); |  | ||||||
|          |  | ||||||
|         // 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; |  | ||||||
|     } |  | ||||||
|      |  | ||||||
|     #endregion |     #endregion | ||||||
|      |      | ||||||
|     #endregion |     #endregion | ||||||
| 
 | 
 | ||||||
|     #region Translation Settings |     #region Culture Settings | ||||||
| 
 | 
 | ||||||
|     #region List of culture indices |     #region List of culture indices | ||||||
|      |      | ||||||
| @ -432,4 +348,64 @@ public static class AppSettings | |||||||
|     #endregion |     #endregion | ||||||
| 
 | 
 | ||||||
|     #endregion |     #endregion | ||||||
|  | 
 | ||||||
|  |     #region Generator Settings | ||||||
|  | 
 | ||||||
|  |     #region Generator Mode | ||||||
|  |      | ||||||
|  |     public static async Task<SettingGeneratorMode> 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<bool> 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<string> 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<string> 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<int> 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<bool> 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<string> 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 | ||||||
| } | } | ||||||
| @ -28,7 +28,7 @@ public static class DeepL | |||||||
|          |          | ||||||
|             return new DeepLUsage(true, usage.Character!.Count, usage.Character.Limit); |             return new DeepLUsage(true, usage.Character!.Count, usage.Character.Limit); | ||||||
|         } |         } | ||||||
|         catch (AuthorizationException e) |         catch (AuthorizationException) | ||||||
|         { |         { | ||||||
|             DEEPL_NOT_AVAILABLE = true; |             DEEPL_NOT_AVAILABLE = true; | ||||||
|             return new DeepLUsage(false, 0, 1, true); |             return new DeepLUsage(false, 0, 1, true); | ||||||
| @ -63,7 +63,7 @@ public static class DeepL | |||||||
|              |              | ||||||
|             return translation.Text; |             return translation.Text; | ||||||
|         } |         } | ||||||
|         catch (AuthorizationException e) |         catch (AuthorizationException) | ||||||
|         { |         { | ||||||
|             DEEPL_NOT_AVAILABLE = true; |             DEEPL_NOT_AVAILABLE = true; | ||||||
|             return string.Empty; |             return string.Empty; | ||||||
|  | |||||||
							
								
								
									
										175
									
								
								I18N Commander/Processor/Generators/DotnetBigFile.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								I18N Commander/Processor/Generators/DotnetBigFile.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,175 @@ | |||||||
|  | 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($"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<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 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<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}}}"); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										41
									
								
								I18N Commander/Processor/Generators/Generator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								I18N Commander/Processor/Generators/Generator.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | |||||||
|  | namespace Processor.Generators; | ||||||
|  | 
 | ||||||
|  | public static class Generator | ||||||
|  | { | ||||||
|  |     private static readonly Dictionary<Type, IGenerator> 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<ProcessorResult<long>> 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<long>(bytesWritten); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								I18N Commander/Processor/Generators/IGenerator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								I18N Commander/Processor/Generators/IGenerator.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | namespace Processor.Generators; | ||||||
|  | 
 | ||||||
|  | public interface IGenerator | ||||||
|  | { | ||||||
|  |     public Task<ProcessorResult<long>> GenerateAsync(); | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								I18N Commander/Processor/Generators/Type.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								I18N Commander/Processor/Generators/Type.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | namespace Processor.Generators; | ||||||
|  | 
 | ||||||
|  | public enum Type | ||||||
|  | { | ||||||
|  |     NONE, | ||||||
|  |      | ||||||
|  |     DOTNET, | ||||||
|  |     GODOT, | ||||||
|  | } | ||||||
							
								
								
									
										6
									
								
								I18N Commander/Processor/Generators/VoidGenerator.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								I18N Commander/Processor/Generators/VoidGenerator.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | namespace Processor.Generators; | ||||||
|  | 
 | ||||||
|  | public class VoidGenerator : IGenerator | ||||||
|  | { | ||||||
|  |     public Task GenerateAsync() => Task.CompletedTask; | ||||||
|  | } | ||||||
| @ -198,4 +198,12 @@ public static class SectionProcessor | |||||||
|          |          | ||||||
|         return $"Section's path: {path}"; |         return $"Section's path: {path}"; | ||||||
|     } |     } | ||||||
|  |      | ||||||
|  |     public static async Task<List<Section>> GetChildSections(string sectionKey) | ||||||
|  |     { | ||||||
|  |         await using var db = ProcessorMeta.ServiceProvider.GetRequiredService<DataContext>(); | ||||||
|  |         var section = await db.Sections.FirstAsync(n => n.DataKey == sectionKey); | ||||||
|  |          | ||||||
|  |         return await db.Sections.Where(n => n.Parent == section).ToListAsync(); | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -124,7 +124,7 @@ public static class TextElementProcessor | |||||||
|             // Save the changes: |             // Save the changes: | ||||||
|             await db.SaveChangesAsync(); |             await db.SaveChangesAsync(); | ||||||
|         } |         } | ||||||
|         catch (DbUpdateException updateException) |         catch (DbUpdateException) | ||||||
|         { |         { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,4 +1,5 @@ | |||||||
| using System.Linq.Expressions; | using System.Linq.Expressions; | ||||||
|  | using System.Text; | ||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
| 
 | 
 | ||||||
| namespace Processor; | namespace Processor; | ||||||
| @ -46,4 +47,25 @@ internal static class Utils | |||||||
| 
 | 
 | ||||||
|         return code; |         return code; | ||||||
|     } |     } | ||||||
|  |      | ||||||
|  |     public static string MadeVerbatimStringLiteral(string text) | ||||||
|  |     { | ||||||
|  |         IEnumerable<char> 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(); | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -4,10 +4,26 @@ namespace UI_WinForms; | |||||||
| 
 | 
 | ||||||
| internal static class AppEvents | 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 |     #region Event: Section was changed | ||||||
| 
 | 
 | ||||||
|     // Section changed event which can be subscribed: |     // Section changed event which can be subscribed: | ||||||
|     internal static event EventHandler<Section> WhenSectionChanged; |     internal static event EventHandler<Section>? WhenSectionChanged; | ||||||
|      |      | ||||||
|     // Method to raise the section changed event: |     // Method to raise the section changed event: | ||||||
|     internal static void SectionChanged(Section section) => WhenSectionChanged?.Invoke(null, section); |     internal static void SectionChanged(Section section) => WhenSectionChanged?.Invoke(null, section); | ||||||
| @ -17,7 +33,7 @@ internal static class AppEvents | |||||||
|     #region Event: Text element was changed |     #region Event: Text element was changed | ||||||
| 
 | 
 | ||||||
|     // Text element changed event which can be subscribed: |     // Text element changed event which can be subscribed: | ||||||
|     internal static event EventHandler<TextElement?> WhenTextElementChanged; |     internal static event EventHandler<TextElement?>? WhenTextElementChanged; | ||||||
|      |      | ||||||
|     // Method to raise the text element changed event: |     // Method to raise the text element changed event: | ||||||
|     internal static void TextElementChanged(TextElement? textElement) => WhenTextElementChanged?.Invoke(null, textElement); |     internal static void TextElementChanged(TextElement? textElement) => WhenTextElementChanged?.Invoke(null, textElement); | ||||||
| @ -27,7 +43,7 @@ internal static class AppEvents | |||||||
|     #region Translation was changed |     #region Translation was changed | ||||||
| 
 | 
 | ||||||
|     // Translation changed event which can be subscribed: |     // Translation changed event which can be subscribed: | ||||||
|     internal static event EventHandler<Translation?> WhenTranslationChanged; |     internal static event EventHandler<Translation?>? WhenTranslationChanged; | ||||||
|      |      | ||||||
|     // Method to raise the translation changed event: |     // Method to raise the translation changed event: | ||||||
|     internal static void TranslationChanged(Translation? translation) => WhenTranslationChanged?.Invoke(null, translation); |     internal static void TranslationChanged(Translation? translation) => WhenTranslationChanged?.Invoke(null, translation); | ||||||
|  | |||||||
| @ -16,6 +16,7 @@ public partial class Main : UserControl | |||||||
|             if(result == DialogResult.Yes) |             if(result == DialogResult.Yes) | ||||||
|             { |             { | ||||||
|                 Program.RestartMainApp = true; |                 Program.RestartMainApp = true; | ||||||
|  |                 AppEvents.ResetAllSubscriptions(); | ||||||
|                 this.ParentForm!.Close(); |                 this.ParentForm!.Close(); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -34,6 +34,7 @@ | |||||||
|             this.buttonAdd = new System.Windows.Forms.Button(); |             this.buttonAdd = new System.Windows.Forms.Button(); | ||||||
|             this.buttonRemove = new System.Windows.Forms.Button(); |             this.buttonRemove = new System.Windows.Forms.Button(); | ||||||
|             this.buttonRename = 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.treeView = new System.Windows.Forms.TreeView(); | ||||||
|             this.toolTip = new System.Windows.Forms.ToolTip(this.components); |             this.toolTip = new System.Windows.Forms.ToolTip(this.components); | ||||||
|             this.tableLayout.SuspendLayout(); |             this.tableLayout.SuspendLayout(); | ||||||
| @ -61,6 +62,7 @@ | |||||||
|             this.flowLayoutBottom.Controls.Add(this.buttonAdd); |             this.flowLayoutBottom.Controls.Add(this.buttonAdd); | ||||||
|             this.flowLayoutBottom.Controls.Add(this.buttonRemove); |             this.flowLayoutBottom.Controls.Add(this.buttonRemove); | ||||||
|             this.flowLayoutBottom.Controls.Add(this.buttonRename); |             this.flowLayoutBottom.Controls.Add(this.buttonRename); | ||||||
|  |             this.flowLayoutBottom.Controls.Add(this.buttonGenerate); | ||||||
|             this.flowLayoutBottom.Dock = System.Windows.Forms.DockStyle.Fill; |             this.flowLayoutBottom.Dock = System.Windows.Forms.DockStyle.Fill; | ||||||
|             this.flowLayoutBottom.Location = new System.Drawing.Point(0, 445); |             this.flowLayoutBottom.Location = new System.Drawing.Point(0, 445); | ||||||
|             this.flowLayoutBottom.Margin = new System.Windows.Forms.Padding(0); |             this.flowLayoutBottom.Margin = new System.Windows.Forms.Padding(0); | ||||||
| @ -115,6 +117,22 @@ | |||||||
|             this.buttonRename.UseVisualStyleBackColor = true; |             this.buttonRename.UseVisualStyleBackColor = true; | ||||||
|             this.buttonRename.Click += new System.EventHandler(this.buttonRename_Click); |             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 |             // treeView | ||||||
|             //  |             //  | ||||||
|             this.treeView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; |             this.treeView.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; | ||||||
| @ -159,5 +177,6 @@ | |||||||
|         private TreeView treeView; |         private TreeView treeView; | ||||||
|         private ToolTip toolTip; |         private ToolTip toolTip; | ||||||
|         private Button buttonRename; |         private Button buttonRename; | ||||||
|  |         private Button buttonGenerate; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,13 +1,16 @@ | |||||||
| using DataModel.Database; | using DataModel.Database; | ||||||
| using Processor; | using Processor; | ||||||
|  | using Processor.Generators; | ||||||
| using UI_WinForms.Dialogs; | using UI_WinForms.Dialogs; | ||||||
| using UI_WinForms.Resources; | using UI_WinForms.Resources; | ||||||
|  | using Timer = System.Timers.Timer; | ||||||
| 
 | 
 | ||||||
| namespace UI_WinForms.Components; | namespace UI_WinForms.Components; | ||||||
| 
 | 
 | ||||||
| public partial class SectionTree : UserControl | public partial class SectionTree : UserControl | ||||||
| { | { | ||||||
|     private static readonly Dictionary<TreeNode, EventHandler<DataModel.Database.Translation?>> NODE_PROGRESS_HANDLERS = new(); |     private static readonly Dictionary<TreeNode, EventHandler<DataModel.Database.Translation?>?> NODE_PROGRESS_HANDLERS = new(); | ||||||
|  |     private readonly Timer generatorTimer; | ||||||
| 
 | 
 | ||||||
|     public SectionTree() |     public SectionTree() | ||||||
|     { |     { | ||||||
| @ -26,10 +29,39 @@ public partial class SectionTree : UserControl | |||||||
|         // Set the image list to the tree view: |         // Set the image list to the tree view: | ||||||
|         this.treeView.ImageList = imgList; |         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: |         // Subscribe to the load event: | ||||||
|         this.Load += this.LoadNodes; |         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) |     private async void LoadNodes(object? sender, EventArgs e) | ||||||
|     { |     { | ||||||
|         if(this.DesignMode) |         if(this.DesignMode) | ||||||
| @ -277,4 +309,31 @@ public partial class SectionTree : UserControl | |||||||
|         selectedNode.Text = alteredSection.Result!.Name; |         selectedNode.Text = alteredSection.Result!.Name; | ||||||
|         selectedNode.Name = alteredSection.Result.DataKey; // [sic] name is the key |         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; | ||||||
|  |     } | ||||||
| } | } | ||||||
| @ -77,6 +77,8 @@ public sealed partial class Setting : UserControl | |||||||
|     { |     { | ||||||
|         if(this.data.ChangeNeedsRestart) |         if(this.data.ChangeNeedsRestart) | ||||||
|             this.needRestart = true; |             this.needRestart = true; | ||||||
|  |          | ||||||
|  |         AppEvents.SettingsChanged(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private void UpdateExplanation() => this.labelExplanation.Text = this.data.SettingExplanation(); |     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: |                     // 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(); |                     setting.UpdateExplanation(); | ||||||
|                 }; |                 }; | ||||||
|                  |                  | ||||||
| @ -257,7 +259,6 @@ public sealed partial class Setting : UserControl | |||||||
|                 }; |                 }; | ||||||
|                  |                  | ||||||
|                 // Setup the change event handler: |                 // Setup the change event handler: | ||||||
|                 dropdown.SelectedValueChanged += (sender, args) => changeTrigger(); |  | ||||||
|                 dropdown.SelectedValueChanged += async (sender, args) => await AppSettings.SetDeepLAction(dropdown.SelectedIndex switch |                 dropdown.SelectedValueChanged += async (sender, args) => await AppSettings.SetDeepLAction(dropdown.SelectedIndex switch | ||||||
|                 { |                 { | ||||||
|                     0 => SettingDeepLAction.MANUAL, |                     0 => SettingDeepLAction.MANUAL, | ||||||
| @ -265,6 +266,7 @@ public sealed partial class Setting : UserControl | |||||||
|                      |                      | ||||||
|                     _ => SettingDeepLAction.MANUAL, |                     _ => SettingDeepLAction.MANUAL, | ||||||
|                 }); |                 }); | ||||||
|  |                 dropdown.SelectedValueChanged += (sender, args) => changeTrigger(); | ||||||
|                  |                  | ||||||
|                 // Apply the desired layout: |                 // Apply the desired layout: | ||||||
|                 dropdown.Dock = DockStyle.Fill; |                 dropdown.Dock = DockStyle.Fill; | ||||||
| @ -314,12 +316,12 @@ public sealed partial class Setting : UserControl | |||||||
|                 dropdown.SelectedIndex = currentCultureDropdownIndex; |                 dropdown.SelectedIndex = currentCultureDropdownIndex; | ||||||
|                  |                  | ||||||
|                 // Setup the change event handler: |                 // Setup the change event handler: | ||||||
|                 dropdown.SelectedValueChanged += (sender, args) => changeTrigger(); |  | ||||||
|                 dropdown.SelectedValueChanged += async (sender, args) => |                 dropdown.SelectedValueChanged += async (sender, args) => | ||||||
|                 { |                 { | ||||||
|                     if(dropdown.SelectedItem is ComboBoxItem selectedItem) |                     if(dropdown.SelectedItem is ComboBoxItem selectedItem) | ||||||
|                         await AppSettings.SetDeepLSourceCultureIndex(selectedItem.CultureIndex); |                         await AppSettings.SetDeepLSourceCultureIndex(selectedItem.CultureIndex); | ||||||
|                 }; |                 }; | ||||||
|  |                 dropdown.SelectedValueChanged += (sender, args) => changeTrigger(); | ||||||
|                  |                  | ||||||
|                 // Apply the desired layout: |                 // Apply the desired layout: | ||||||
|                 dropdown.Dock = DockStyle.Fill; |                 dropdown.Dock = DockStyle.Fill; | ||||||
| @ -388,14 +390,314 @@ public sealed partial class Setting : UserControl | |||||||
|             cultureIndices.Remove(innerLoopIndex); |             cultureIndices.Remove(innerLoopIndex); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |      | ||||||
|  |     private static async Task<Setting> 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<Setting> 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<Setting> 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<Setting> 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<Setting> 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<Setting> 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<Setting> 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<Task<Setting>> GetAllSettings() |     public static IEnumerable<Task<Setting>> 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(); |         yield return ShowDeepLSourceCultureSettingAsync(); | ||||||
|         foreach (var setting in ShowCultureSettingsAsync()) |         foreach (var setting in ShowCultureSettingsAsync()) | ||||||
|         { |  | ||||||
|             yield return setting; |             yield return setting; | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         yield return ShowDeepLUsageSettingAsync(); |         yield return ShowDeepLUsageSettingAsync(); | ||||||
|         yield return ShowDeepLActionSettingAsync(); |         yield return ShowDeepLActionSettingAsync(); | ||||||
|  | |||||||
| @ -22,6 +22,9 @@ public sealed partial class Translation : UserControl | |||||||
|     { |     { | ||||||
|         this.InitializeComponent(); |         this.InitializeComponent(); | ||||||
|         this.Dock = DockStyle.Top; |         this.Dock = DockStyle.Top; | ||||||
|  |          | ||||||
|  |         this.saveTimer = new Timer(); | ||||||
|  |         this.translationTimer = new Timer(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public Translation(AppSettings.CultureInfo cultureInfo) |     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) |         if (this.currentTranslationId > -1) | ||||||
|         { |         { | ||||||
| @ -135,7 +138,7 @@ public sealed partial class Translation : UserControl | |||||||
|         this.isManualOnlyMode = this.checkBoxManual.Checked; |         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) |         if (this.currentTranslationId < 0 || this.isDeepLSourceCulture == false) | ||||||
|             return; |             return; | ||||||
|  | |||||||
| @ -6,7 +6,8 @@ public static class ExtensionsError | |||||||
| { | { | ||||||
|     public static void ProcessError<TResult>(this ProcessorResult<TResult> result) |     public static void ProcessError<TResult>(this ProcessorResult<TResult> result) | ||||||
|     { |     { | ||||||
|         if (result.Successful) return; |         if (result.Successful) | ||||||
|  |             return; | ||||||
| 
 | 
 | ||||||
|         MessageBox.Show(result.ErrorMessage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); |         MessageBox.Show(result.ErrorMessage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -140,6 +140,26 @@ namespace UI_WinForms.Resources { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   Looks up a localized resource of type System.Drawing.Bitmap. | ||||||
|  |         /// </summary> | ||||||
|  |         internal static System.Drawing.Bitmap icons8_code_512 { | ||||||
|  |             get { | ||||||
|  |                 object obj = ResourceManager.GetObject("icons8_code_512", resourceCulture); | ||||||
|  |                 return ((System.Drawing.Bitmap)(obj)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   Looks up a localized resource of type System.Drawing.Bitmap. | ||||||
|  |         /// </summary> | ||||||
|  |         internal static System.Drawing.Bitmap icons8_code_512__2_ { | ||||||
|  |             get { | ||||||
|  |                 object obj = ResourceManager.GetObject("icons8_code_512__2_", resourceCulture); | ||||||
|  |                 return ((System.Drawing.Bitmap)(obj)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         ///   Looks up a localized resource of type System.Drawing.Bitmap. |         ///   Looks up a localized resource of type System.Drawing.Bitmap. | ||||||
|         /// </summary> |         /// </summary> | ||||||
| @ -180,6 +200,16 @@ namespace UI_WinForms.Resources { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  |         /// <summary> | ||||||
|  |         ///   Looks up a localized resource of type System.Drawing.Bitmap. | ||||||
|  |         /// </summary> | ||||||
|  |         internal static System.Drawing.Bitmap icons8_folder_tree_512 { | ||||||
|  |             get { | ||||||
|  |                 object obj = ResourceManager.GetObject("icons8_folder_tree_512", resourceCulture); | ||||||
|  |                 return ((System.Drawing.Bitmap)(obj)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |          | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         ///   Looks up a localized resource of type System.Drawing.Bitmap. |         ///   Looks up a localized resource of type System.Drawing.Bitmap. | ||||||
|         /// </summary> |         /// </summary> | ||||||
|  | |||||||
| @ -142,6 +142,12 @@ | |||||||
|   <data name="icons8_chat_bubble_512" type="System.Resources.ResXFileRef, System.Windows.Forms"> |   <data name="icons8_chat_bubble_512" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||||
|     <value>icons8-chat-bubble-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> |     <value>icons8-chat-bubble-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> | ||||||
|   </data> |   </data> | ||||||
|  |   <data name="icons8_code_512" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||||
|  |     <value>icons8-code-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> | ||||||
|  |   </data> | ||||||
|  |   <data name="icons8_code_512__2_" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||||
|  |     <value>icons8-code-512 (2).png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> | ||||||
|  |   </data> | ||||||
|   <data name="icons8_collectibles_512" type="System.Resources.ResXFileRef, System.Windows.Forms"> |   <data name="icons8_collectibles_512" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||||
|     <value>icons8-collectibles-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> |     <value>icons8-collectibles-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> | ||||||
|   </data> |   </data> | ||||||
| @ -154,6 +160,9 @@ | |||||||
|   <data name="icons8_document_512" type="System.Resources.ResXFileRef, System.Windows.Forms"> |   <data name="icons8_document_512" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||||
|     <value>icons8-document-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> |     <value>icons8-document-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> | ||||||
|   </data> |   </data> | ||||||
|  |   <data name="icons8_folder_tree_512" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||||
|  |     <value>icons8-folder-tree-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> | ||||||
|  |   </data> | ||||||
|   <data name="icons8_increase_512" type="System.Resources.ResXFileRef, System.Windows.Forms"> |   <data name="icons8_increase_512" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||||
|     <value>icons8-increase-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> |     <value>icons8-increase-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> | ||||||
|   </data> |   </data> | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								I18N Commander/UI WinForms/Resources/icons8-code-512 (2).png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								I18N Commander/UI WinForms/Resources/icons8-code-512 (2).png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 4.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								I18N Commander/UI WinForms/Resources/icons8-code-512.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								I18N Commander/UI WinForms/Resources/icons8-code-512.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 4.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								I18N Commander/UI WinForms/Resources/icons8-folder-tree-512.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								I18N Commander/UI WinForms/Resources/icons8-folder-tree-512.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.4 KiB | 
		Loading…
	
		Reference in New Issue
	
	Block a user