diff --git a/I18N Commander/DataModel/Database/Common/DataContext.cs b/I18N Commander/DataModel/Database/Common/DataContext.cs index 40e0453..c277cb1 100644 --- a/I18N Commander/DataModel/Database/Common/DataContext.cs +++ b/I18N Commander/DataModel/Database/Common/DataContext.cs @@ -58,6 +58,7 @@ public sealed class DataContext : DbContext, IDataContext modelBuilder.Entity().HasIndex(n => n.Id); modelBuilder.Entity().HasIndex(n => n.Culture); modelBuilder.Entity().HasIndex(n => n.Text); + modelBuilder.Entity().HasIndex(n => n.TranslateManual); modelBuilder.Entity().Navigation(n => n.TextElement).AutoInclude(); #endregion diff --git a/I18N Commander/DataModel/Database/Translation.cs b/I18N Commander/DataModel/Database/Translation.cs index 2b5b874..86d172a 100644 --- a/I18N Commander/DataModel/Database/Translation.cs +++ b/I18N Commander/DataModel/Database/Translation.cs @@ -9,4 +9,6 @@ public sealed class Translation public string Culture { get; set; } = "en-US"; public string Text { get; set; } = string.Empty; + + public bool TranslateManual { get; set; } = false; } \ No newline at end of file diff --git a/I18N Commander/DataModel/Migrations/20220921182526_202209AddManualFlag.Designer.cs b/I18N Commander/DataModel/Migrations/20220921182526_202209AddManualFlag.Designer.cs new file mode 100644 index 0000000..9503b9c --- /dev/null +++ b/I18N Commander/DataModel/Migrations/20220921182526_202209AddManualFlag.Designer.cs @@ -0,0 +1,211 @@ +// +using System; +using DataModel.Database.Common; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace DataModel.Migrations +{ + [DbContext(typeof(DataContext))] + [Migration("20220921182526_202209AddManualFlag")] + partial class _202209AddManualFlag + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "6.0.9"); + + modelBuilder.Entity("DataModel.Database.Section", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DataKey") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Depth") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("ParentId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("DataKey"); + + b.HasIndex("Depth"); + + b.HasIndex("Id"); + + b.HasIndex("Name"); + + b.HasIndex("ParentId"); + + b.ToTable("Sections"); + }); + + modelBuilder.Entity("DataModel.Database.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("BoolValue") + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("GuidValue") + .HasColumnType("TEXT"); + + b.Property("IntegerValue") + .HasColumnType("INTEGER"); + + b.Property("TextValue") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("BoolValue"); + + b.HasIndex("Code") + .IsUnique(); + + b.HasIndex("GuidValue"); + + b.HasIndex("Id"); + + b.HasIndex("IntegerValue"); + + b.HasIndex("TextValue"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("DataModel.Database.TextElement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Code") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("IsMultiLine") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("SectionId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("Id"); + + b.HasIndex("IsMultiLine"); + + b.HasIndex("Name"); + + b.HasIndex("SectionId"); + + b.ToTable("TextElements"); + }); + + modelBuilder.Entity("DataModel.Database.Translation", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Culture") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Text") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TextElementId") + .HasColumnType("INTEGER"); + + b.Property("TranslateManual") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Culture"); + + b.HasIndex("Id"); + + b.HasIndex("Text"); + + b.HasIndex("TextElementId"); + + b.HasIndex("TranslateManual"); + + b.ToTable("Translations"); + }); + + modelBuilder.Entity("DataModel.Database.Section", b => + { + b.HasOne("DataModel.Database.Section", "Parent") + .WithMany() + .HasForeignKey("ParentId"); + + b.Navigation("Parent"); + }); + + modelBuilder.Entity("DataModel.Database.TextElement", b => + { + b.HasOne("DataModel.Database.Section", "Section") + .WithMany("TextElements") + .HasForeignKey("SectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Section"); + }); + + modelBuilder.Entity("DataModel.Database.Translation", b => + { + b.HasOne("DataModel.Database.TextElement", "TextElement") + .WithMany("Translations") + .HasForeignKey("TextElementId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("TextElement"); + }); + + modelBuilder.Entity("DataModel.Database.Section", b => + { + b.Navigation("TextElements"); + }); + + modelBuilder.Entity("DataModel.Database.TextElement", b => + { + b.Navigation("Translations"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/I18N Commander/DataModel/Migrations/20220921182526_202209AddManualFlag.cs b/I18N Commander/DataModel/Migrations/20220921182526_202209AddManualFlag.cs new file mode 100644 index 0000000..d068c51 --- /dev/null +++ b/I18N Commander/DataModel/Migrations/20220921182526_202209AddManualFlag.cs @@ -0,0 +1,35 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace DataModel.Migrations +{ + public partial class _202209AddManualFlag : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "TranslateManual", + table: "Translations", + type: "INTEGER", + nullable: false, + defaultValue: false); + + migrationBuilder.CreateIndex( + name: "IX_Translations_TranslateManual", + table: "Translations", + column: "TranslateManual"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Translations_TranslateManual", + table: "Translations"); + + migrationBuilder.DropColumn( + name: "TranslateManual", + table: "Translations"); + } + } +} diff --git a/I18N Commander/DataModel/Migrations/DataContextModelSnapshot.cs b/I18N Commander/DataModel/Migrations/DataContextModelSnapshot.cs index 091a4e5..4a0c76a 100644 --- a/I18N Commander/DataModel/Migrations/DataContextModelSnapshot.cs +++ b/I18N Commander/DataModel/Migrations/DataContextModelSnapshot.cs @@ -145,6 +145,9 @@ namespace DataModel.Migrations b.Property("TextElementId") .HasColumnType("INTEGER"); + b.Property("TranslateManual") + .HasColumnType("INTEGER"); + b.HasKey("Id"); b.HasIndex("Culture"); @@ -155,6 +158,8 @@ namespace DataModel.Migrations b.HasIndex("TextElementId"); + b.HasIndex("TranslateManual"); + b.ToTable("Translations"); }); diff --git a/I18N Commander/Processor/TranslationProcessor.cs b/I18N Commander/Processor/TranslationProcessor.cs index 42e19e9..f2d2494 100644 --- a/I18N Commander/Processor/TranslationProcessor.cs +++ b/I18N Commander/Processor/TranslationProcessor.cs @@ -17,6 +17,7 @@ public static class TranslationProcessor if(!await db.Translations.AnyAsync(n => n.TextElement == textElement && n.Culture == cultureInfo.Code)) missedTranslations.Add(new Translation { + TranslateManual = false, Culture = cultureInfo.Code, Text = string.Empty, }); @@ -32,11 +33,12 @@ public static class TranslationProcessor return await db.Translations.Where(n => n.TextElement == textElement).ToListAsync(); } - public static async Task> SaveChangedTranslation(int translationId, string text) + public static async Task> SaveChangedTranslation(int translationId, string text, bool manualFLag) { await using var db = ProcessorMeta.ServiceProvider.GetRequiredService(); var dbTranslation = await db.Translations.FirstAsync(n => n.Id == translationId); dbTranslation.Text = text; + dbTranslation.TranslateManual = manualFLag; try { diff --git a/I18N Commander/UI WinForms/Components/Translation.Designer.cs b/I18N Commander/UI WinForms/Components/Translation.Designer.cs index 1a260fe..f86dcf9 100644 --- a/I18N Commander/UI WinForms/Components/Translation.Designer.cs +++ b/I18N Commander/UI WinForms/Components/Translation.Designer.cs @@ -32,9 +32,12 @@ this.tableLayout = new System.Windows.Forms.TableLayoutPanel(); this.labelHead = new System.Windows.Forms.Label(); this.textBox = new System.Windows.Forms.TextBox(); + this.flowLayoutHeaderTools = new System.Windows.Forms.FlowLayoutPanel(); this.buttonDeepL = new System.Windows.Forms.Button(); + this.checkBoxManual = new System.Windows.Forms.CheckBox(); this.toolTip = new System.Windows.Forms.ToolTip(this.components); this.tableLayout.SuspendLayout(); + this.flowLayoutHeaderTools.SuspendLayout(); this.SuspendLayout(); // // tableLayout @@ -44,7 +47,7 @@ this.tableLayout.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayout.Controls.Add(this.labelHead, 0, 0); this.tableLayout.Controls.Add(this.textBox, 0, 1); - this.tableLayout.Controls.Add(this.buttonDeepL, 1, 0); + this.tableLayout.Controls.Add(this.flowLayoutHeaderTools, 1, 0); this.tableLayout.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayout.Location = new System.Drawing.Point(0, 0); this.tableLayout.Name = "tableLayout"; @@ -76,6 +79,18 @@ this.textBox.TabIndex = 1; this.textBox.TextChanged += new System.EventHandler(this.textBox_TextChanged); // + // flowLayoutHeaderTools + // + this.flowLayoutHeaderTools.Controls.Add(this.buttonDeepL); + this.flowLayoutHeaderTools.Controls.Add(this.checkBoxManual); + this.flowLayoutHeaderTools.Dock = System.Windows.Forms.DockStyle.Fill; + this.flowLayoutHeaderTools.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft; + this.flowLayoutHeaderTools.Location = new System.Drawing.Point(366, 0); + this.flowLayoutHeaderTools.Margin = new System.Windows.Forms.Padding(0); + this.flowLayoutHeaderTools.Name = "flowLayoutHeaderTools"; + this.flowLayoutHeaderTools.Size = new System.Drawing.Size(454, 66); + this.flowLayoutHeaderTools.TabIndex = 3; + // // buttonDeepL // this.buttonDeepL.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); @@ -83,7 +98,7 @@ this.buttonDeepL.FlatAppearance.BorderSize = 0; this.buttonDeepL.FlatStyle = System.Windows.Forms.FlatStyle.Flat; this.buttonDeepL.Image = global::UI_WinForms.Resources.Icons.icons8_bot_512; - this.buttonDeepL.Location = new System.Drawing.Point(757, 3); + this.buttonDeepL.Location = new System.Drawing.Point(391, 3); this.buttonDeepL.Name = "buttonDeepL"; this.buttonDeepL.Size = new System.Drawing.Size(60, 60); this.buttonDeepL.TabIndex = 2; @@ -91,6 +106,19 @@ this.buttonDeepL.UseVisualStyleBackColor = true; this.buttonDeepL.Click += new System.EventHandler(this.buttonDeepL_Click); // + // checkBoxManual + // + this.checkBoxManual.AutoSize = true; + this.checkBoxManual.Location = new System.Drawing.Point(188, 3); + this.checkBoxManual.Name = "checkBoxManual"; + this.checkBoxManual.Padding = new System.Windows.Forms.Padding(0, 14, 0, 14); + this.checkBoxManual.Size = new System.Drawing.Size(197, 60); + this.checkBoxManual.TabIndex = 0; + this.checkBoxManual.Text = "Manual translation"; + this.toolTip.SetToolTip(this.checkBoxManual, "Never overwrites this translation automatically"); + this.checkBoxManual.UseVisualStyleBackColor = true; + this.checkBoxManual.CheckedChanged += new System.EventHandler(this.checkBoxManual_CheckedChanged); + // // toolTip // this.toolTip.AutoPopDelay = 30000; @@ -109,6 +137,8 @@ this.Size = new System.Drawing.Size(820, 279); this.tableLayout.ResumeLayout(false); this.tableLayout.PerformLayout(); + this.flowLayoutHeaderTools.ResumeLayout(false); + this.flowLayoutHeaderTools.PerformLayout(); this.ResumeLayout(false); } @@ -120,5 +150,7 @@ private TextBox textBox; private Button buttonDeepL; private ToolTip toolTip; + private FlowLayoutPanel flowLayoutHeaderTools; + private CheckBox checkBoxManual; } } diff --git a/I18N Commander/UI WinForms/Components/Translation.cs b/I18N Commander/UI WinForms/Components/Translation.cs index 6dfac30..f823876 100644 --- a/I18N Commander/UI WinForms/Components/Translation.cs +++ b/I18N Commander/UI WinForms/Components/Translation.cs @@ -15,6 +15,7 @@ public sealed partial class Translation : UserControl private bool isLoading = false; private int currentTranslationId = -1; private bool isDeepLSourceCulture = false; + private bool isManualOnlyMode = false; private Translation? mainCultureTranslation = null; public Translation() @@ -50,6 +51,7 @@ public sealed partial class Translation : UserControl { this.isDeepLSourceCulture = await AppSettings.GetDeepLSourceCultureIndex() == cultureInfo.Index; this.textBox.ReadOnly = !(this.isDeepLSourceCulture || await AppSettings.GetDeepLAction() == SettingDeepLAction.MANUAL); + this.checkBoxManual.Visible = !this.isDeepLSourceCulture; this.buttonDeepL.Visible = await AppSettings.GetDeepLMode() != SettingDeepLMode.DISABLED; this.buttonDeepL.Image = this.isDeepLSourceCulture ? Icons.icons8_trigger_1__svg : Icons.deepl_logo_icon_170284; @@ -66,9 +68,21 @@ public sealed partial class Translation : UserControl { this.isLoading = true; + this.isManualOnlyMode = this.checkBoxManual.Checked = translation.TranslateManual; this.currentTranslationId = translation.Id; - this.textBox.Text = translation.Text; - this.textBox.Multiline = translation.TextElement.IsMultiLine; + + try + { + this.textBox.Text = translation.Text; + this.textBox.Multiline = translation.TextElement.IsMultiLine; + this.textBox.ReadOnly = !(this.isDeepLSourceCulture || await AppSettings.GetDeepLAction() == SettingDeepLAction.MANUAL); + if (this.isManualOnlyMode) + this.textBox.ReadOnly = false; + } + catch (ObjectDisposedException) + { + } + this.Height = translation.TextElement.IsMultiLine ? 280 : 106; this.buttonDeepL.Visible = await AppSettings.GetDeepLMode() != SettingDeepLMode.DISABLED; @@ -110,10 +124,12 @@ public sealed partial class Translation : UserControl private async void SaveChanges(object? sender, ElapsedEventArgs e) { if (this.currentTranslationId > -1) - await TranslationProcessor.SaveChangedTranslation(this.currentTranslationId, this.textBox.Text); + await TranslationProcessor.SaveChangedTranslation(this.currentTranslationId, this.textBox.Text, this.checkBoxManual.Checked); if(this.ParentForm is UI_WinForms.Main main) main.RemoveDeferredSaveOperation(this.currentTranslationId); + + this.isManualOnlyMode = this.checkBoxManual.Checked; } private async void TriggerTranslateNow(object? sender, ElapsedEventArgs e) @@ -129,11 +145,14 @@ public sealed partial class Translation : UserControl internal string GetCurrentText() => this.textBox.Text; - internal async Task Translate() + internal async Task Translate(bool wasManualTriggered = false) { if (this.mainCultureTranslation is null) return; + if(this.isManualOnlyMode && !wasManualTriggered) + return; + var text = await Processor.DeepL.Translate(this.mainCultureTranslation.GetCurrentText(), this.culture); if (this.textBox.InvokeRequired) this.textBox.Invoke(() => this.textBox.Text = text); @@ -146,6 +165,17 @@ public sealed partial class Translation : UserControl if (this.isDeepLSourceCulture) this.TriggerTranslateNow(null, null); else - await this.Translate(); + await this.Translate(wasManualTriggered: true); + } + + private async void checkBoxManual_CheckedChanged(object sender, EventArgs e) + { + if(this.isLoading) + return; + + this.SaveChanges(null, null); + this.textBox.ReadOnly = !(this.isDeepLSourceCulture || await AppSettings.GetDeepLAction() == SettingDeepLAction.MANUAL); + if (this.isManualOnlyMode) + this.textBox.ReadOnly = false; } } \ No newline at end of file