diff --git a/I18N Commander/DataModel/DataModel.csproj b/I18N Commander/DataModel/DataModel.csproj
index 07d4dfb..2b1e1d6 100644
--- a/I18N Commander/DataModel/DataModel.csproj
+++ b/I18N Commander/DataModel/DataModel.csproj
@@ -8,12 +8,12 @@
-
-
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/I18N Commander/Processor/DeepL.cs b/I18N Commander/Processor/DeepL.cs
new file mode 100644
index 0000000..bbfe58e
--- /dev/null
+++ b/I18N Commander/Processor/DeepL.cs
@@ -0,0 +1,66 @@
+using DataModel.Database;
+using DeepL;
+
+namespace Processor;
+
+public static class DeepL
+{
+ private static bool DEEPL_NOT_AVAILABLE = false;
+
+ public readonly record struct DeepLUsage(bool Enabled, long CharacterCount, long CharacterLimit, bool AuthIssue = false);
+
+ public static async Task GetUsage()
+ {
+ if(DEEPL_NOT_AVAILABLE)
+ return new DeepLUsage(false, 0, 1);
+
+ if (await AppSettings.GetDeepLMode() == SettingDeepLMode.DISABLED)
+ return new DeepLUsage(false, 0, 1);
+
+ var deepLAPIKey = await AppSettings.GetDeepLAPIKey();
+ if(string.IsNullOrWhiteSpace(deepLAPIKey))
+ return new DeepLUsage(false, 0, 1);
+
+ try
+ {
+ using var deepl = new Translator(deepLAPIKey);
+ var usage = await deepl.GetUsageAsync();
+
+ return new DeepLUsage(true, usage.Character!.Count, usage.Character.Limit);
+ }
+ catch (AuthorizationException e)
+ {
+ DEEPL_NOT_AVAILABLE = true;
+ return new DeepLUsage(false, 0, 1, true);
+ }
+ }
+
+ public static async Task Translate(string text, string targetCulture)
+ {
+ if (DEEPL_NOT_AVAILABLE)
+ return string.Empty;
+
+ if (await AppSettings.GetDeepLMode() == SettingDeepLMode.DISABLED)
+ return string.Empty;
+
+ var deepLAPIKey = await AppSettings.GetDeepLAPIKey();
+ if(string.IsNullOrWhiteSpace(deepLAPIKey))
+ return string.Empty;
+
+ try
+ {
+ var sourceCultureIndex = await AppSettings.GetDeepLSourceCultureIndex();
+ var sourceCulture = await AppSettings.GetCultureCode(sourceCultureIndex);
+
+ using var deepl = new Translator(deepLAPIKey);
+ var translation = await deepl.TranslateTextAsync(text, sourceCulture, targetCulture);
+
+ return translation.Text;
+ }
+ catch (AuthorizationException e)
+ {
+ DEEPL_NOT_AVAILABLE = true;
+ return string.Empty;
+ }
+ }
+}
\ No newline at end of file
diff --git a/I18N Commander/Processor/Processor.csproj b/I18N Commander/Processor/Processor.csproj
index 94a5986..7b794fd 100644
--- a/I18N Commander/Processor/Processor.csproj
+++ b/I18N Commander/Processor/Processor.csproj
@@ -11,4 +11,8 @@
+
+
+
+
diff --git a/I18N Commander/UI WinForms/Components/Setting.cs b/I18N Commander/UI WinForms/Components/Setting.cs
index 639487b..f9d7e2a 100644
--- a/I18N Commander/UI WinForms/Components/Setting.cs
+++ b/I18N Commander/UI WinForms/Components/Setting.cs
@@ -6,6 +6,8 @@ namespace UI_WinForms.Components;
public sealed partial class Setting : UserControl
{
+ private SettingUIData data;
+
public Setting()
{
this.InitializeComponent();
@@ -15,6 +17,7 @@ public sealed partial class Setting : UserControl
{
this.InitializeComponent();
this.Dock = DockStyle.Top;
+ this.data = settingMetaData;
this.labelIcon.Image = settingMetaData.Icon;
this.labelSettingName.Text = settingMetaData.SettingName();
this.labelExplanation.Text = settingMetaData.SettingExplanation();
@@ -38,6 +41,8 @@ public sealed partial class Setting : UserControl
dataControl.Margin = new Padding(0, (int) margin, 0, (int)margin);
};
}
+
+ private void UpdateExplanation() => this.labelExplanation.Text = this.data.SettingExplanation();
private readonly record struct SettingUIData(
Bitmap Icon,
@@ -109,6 +114,85 @@ public sealed partial class Setting : UserControl
return new Setting(settingData);
}
+ private static async Task ShowDeepLUsageSettingAsync()
+ {
+ var currentUsage = await Processor.DeepL.GetUsage();
+ var percent = currentUsage.Enabled ? currentUsage.CharacterCount / (float)currentUsage.CharacterLimit : 0;
+
+ // Local function to show & update the explanation text:
+ string GetUsageText() => currentUsage.Enabled ?
+ $"You used {currentUsage.CharacterCount:###,###,###,##0} characters out of {currentUsage.CharacterLimit:###,###,###,##0} ({percent:P2})." : currentUsage.AuthIssue ?
+ "Was not able to authorize with DeepL. Please check your API key." :
+ "DeepL is disabled or the API key is not set.";
+
+ var settingData = new SettingUIData(
+ Icon: Icons.icons8_increase_512,
+ SettingName: () => "DeepL Usage",
+ SettingExplanation: GetUsageText,
+ SetupDataControl: () =>
+ {
+ var progressbar = new ProgressBar();
+ progressbar.Maximum = 100;
+ progressbar.Margin = new Padding(0, 16, 0, 16);
+ progressbar.Dock = DockStyle.Fill;
+ progressbar.Style = ProgressBarStyle.Continuous;
+ progressbar.Value = percent switch
+ {
+ < 0 => 0,
+ > 1 => 100,
+
+ _ => (int)(percent * 100)
+ };
+
+ var reloadButton = new Button();
+ reloadButton.Text = string.Empty;
+ reloadButton.Image = Icons.icons8_reload_512;
+ reloadButton.FlatStyle = FlatStyle.Flat;
+ reloadButton.FlatAppearance.BorderSize = 0;
+ reloadButton.BackColor = Color.Empty;
+ reloadButton.UseVisualStyleBackColor = true;
+ reloadButton.Size = new Size(60, 60);
+ reloadButton.Click += async (sender, args) =>
+ {
+ var usage = await Processor.DeepL.GetUsage();
+
+ // Update the outer variables:
+ percent = usage.Enabled ? usage.CharacterCount / (float)usage.CharacterLimit : 0;
+ currentUsage = usage;
+
+ // Update the progress bar:
+ progressbar.Value = percent switch
+ {
+ < 0 => 0,
+ > 1 => 100,
+
+ _ => (int)(percent * 100)
+ };
+
+ // 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;
+ setting.UpdateExplanation();
+ };
+
+ // Setup the layout:
+ var layout = new TableLayoutPanel();
+ layout.ColumnCount = 2;
+ layout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
+ layout.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 66));
+ layout.RowCount = 1;
+ layout.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
+ layout.Controls.Add(progressbar, 0, 0);
+ layout.Controls.Add(reloadButton, 1, 0);
+ layout.Dock = DockStyle.Fill;
+
+ return layout;
+ }
+ );
+
+ var setting = new Setting(settingData);
+ return setting;
+ }
+
private static async Task ShowDeepLActionSettingAsync()
{
var currentSetting = await AppSettings.GetDeepLAction();
@@ -264,7 +348,8 @@ public sealed partial class Setting : UserControl
{
yield return setting;
}
-
+
+ yield return ShowDeepLUsageSettingAsync();
yield return ShowDeepLActionSettingAsync();
yield return ShowDeepLAPIKeySettingAsync();
yield return ShowDeepLModeSettingAsync();
diff --git a/I18N Commander/UI WinForms/Resources/Icons.Designer.cs b/I18N Commander/UI WinForms/Resources/Icons.Designer.cs
index ab02ede..eec4a58 100644
--- a/I18N Commander/UI WinForms/Resources/Icons.Designer.cs
+++ b/I18N Commander/UI WinForms/Resources/Icons.Designer.cs
@@ -170,6 +170,16 @@ namespace UI_WinForms.Resources {
}
}
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap icons8_increase_512 {
+ get {
+ object obj = ResourceManager.GetObject("icons8_increase_512", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
///
/// Looks up a localized resource of type System.Drawing.Bitmap.
///
@@ -230,6 +240,26 @@ namespace UI_WinForms.Resources {
}
}
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap icons8_refresh_512 {
+ get {
+ object obj = ResourceManager.GetObject("icons8_refresh_512", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
+ ///
+ /// Looks up a localized resource of type System.Drawing.Bitmap.
+ ///
+ internal static System.Drawing.Bitmap icons8_reload_512 {
+ get {
+ object obj = ResourceManager.GetObject("icons8_reload_512", resourceCulture);
+ return ((System.Drawing.Bitmap)(obj));
+ }
+ }
+
///
/// Looks up a localized resource of type System.Drawing.Bitmap.
///
diff --git a/I18N Commander/UI WinForms/Resources/Icons.resx b/I18N Commander/UI WinForms/Resources/Icons.resx
index f4a76ae..ccb480d 100644
--- a/I18N Commander/UI WinForms/Resources/Icons.resx
+++ b/I18N Commander/UI WinForms/Resources/Icons.resx
@@ -151,6 +151,9 @@
icons8-document-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ icons8-increase-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
icons8-key-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
@@ -169,6 +172,12 @@
icons8-play-512 (2).png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+ icons8-refresh-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ icons8-reload-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
icons8-remove-tag-512.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
diff --git a/I18N Commander/UI WinForms/Resources/icons8-increase-512.png b/I18N Commander/UI WinForms/Resources/icons8-increase-512.png
new file mode 100644
index 0000000..e8cccca
Binary files /dev/null and b/I18N Commander/UI WinForms/Resources/icons8-increase-512.png differ
diff --git a/I18N Commander/UI WinForms/Resources/icons8-refresh-512.png b/I18N Commander/UI WinForms/Resources/icons8-refresh-512.png
new file mode 100644
index 0000000..c496bae
Binary files /dev/null and b/I18N Commander/UI WinForms/Resources/icons8-refresh-512.png differ
diff --git a/I18N Commander/UI WinForms/Resources/icons8-reload-512.png b/I18N Commander/UI WinForms/Resources/icons8-reload-512.png
new file mode 100644
index 0000000..346a4b9
Binary files /dev/null and b/I18N Commander/UI WinForms/Resources/icons8-reload-512.png differ