├── .gitignore ├── README.md └── src ├── .vs └── XmlContentTranslator │ └── v16 │ └── .suo ├── XmlContentTranslator.sln └── XmlContentTranslator ├── Find.Designer.cs ├── Find.cs ├── Find.resx ├── ListViewItemComparer.cs ├── Main.Designer.cs ├── Main.cs ├── Main.resx ├── Program.cs ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── StringUtils.cs ├── Translator ├── Configuration.cs ├── Formatting.cs ├── GoogleTranslator1.cs ├── HtmlUtil.cs ├── ITranslator.cs ├── Json.cs ├── StringExtensions.cs ├── TranslationHelper.cs ├── TranslationPair.cs └── Utilities.cs ├── XmlContentTranslator.csproj ├── XmlContentTranslator.csproj.user ├── XmlUtils.cs ├── app.config ├── file.ico ├── xct.ico └── xct2.ico /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | /src/XmlContentTranslator/XmlContentTranslator.csproj.DotSettings 4 | /src/XmlContentTranslator/XmlContentTranslator.zip 5 | /src/.vs/XmlContentTranslator/v15 6 | /src/.vs/XmlContentTranslator 7 | /xct.pdn 8 | /xct.png 9 | /xct2.ico 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Xml Content Translator 2 | 3 | A tool for editing Subtitle Edit translations (or other xml files) 4 | -------------------------------------------------------------------------------- /src/.vs/XmlContentTranslator/v16/.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubtitleEdit/XmlContentTranslator/1941f23b4abfbe64956f34b6b7b1c9e9f1f1c43a/src/.vs/XmlContentTranslator/v16/.suo -------------------------------------------------------------------------------- /src/XmlContentTranslator.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XmlContentTranslator", "XmlContentTranslator\XmlContentTranslator.csproj", "{79BA7CB6-5613-4527-89C3-A4FF42810390}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {79BA7CB6-5613-4527-89C3-A4FF42810390}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {79BA7CB6-5613-4527-89C3-A4FF42810390}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {79BA7CB6-5613-4527-89C3-A4FF42810390}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {79BA7CB6-5613-4527-89C3-A4FF42810390}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Find.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace XmlContentTranslator 2 | { 3 | partial class Find 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.buttonFind = new System.Windows.Forms.Button(); 32 | this.label1 = new System.Windows.Forms.Label(); 33 | this.textBox1 = new System.Windows.Forms.TextBox(); 34 | this.radioButtonTags = new System.Windows.Forms.RadioButton(); 35 | this.groupBoxSearchIn = new System.Windows.Forms.GroupBox(); 36 | this.radioButtonText = new System.Windows.Forms.RadioButton(); 37 | this.button1 = new System.Windows.Forms.Button(); 38 | this.groupBoxSearchIn.SuspendLayout(); 39 | this.SuspendLayout(); 40 | // 41 | // buttonFind 42 | // 43 | this.buttonFind.Location = new System.Drawing.Point(244, 25); 44 | this.buttonFind.Name = "buttonFind"; 45 | this.buttonFind.Size = new System.Drawing.Size(98, 23); 46 | this.buttonFind.TabIndex = 3; 47 | this.buttonFind.Text = "&Find"; 48 | this.buttonFind.UseVisualStyleBackColor = true; 49 | this.buttonFind.Click += new System.EventHandler(this.buttonFind_Click); 50 | // 51 | // label1 52 | // 53 | this.label1.AutoSize = true; 54 | this.label1.Location = new System.Drawing.Point(12, 9); 55 | this.label1.Name = "label1"; 56 | this.label1.Size = new System.Drawing.Size(56, 13); 57 | this.label1.TabIndex = 0; 58 | this.label1.Text = "Find what:"; 59 | // 60 | // textBox1 61 | // 62 | this.textBox1.Location = new System.Drawing.Point(12, 25); 63 | this.textBox1.Name = "textBox1"; 64 | this.textBox1.Size = new System.Drawing.Size(226, 20); 65 | this.textBox1.TabIndex = 1; 66 | // 67 | // radioButtonTags 68 | // 69 | this.radioButtonTags.AutoSize = true; 70 | this.radioButtonTags.Location = new System.Drawing.Point(19, 19); 71 | this.radioButtonTags.Name = "radioButtonTags"; 72 | this.radioButtonTags.Size = new System.Drawing.Size(49, 17); 73 | this.radioButtonTags.TabIndex = 0; 74 | this.radioButtonTags.TabStop = true; 75 | this.radioButtonTags.Text = "Tags"; 76 | this.radioButtonTags.UseVisualStyleBackColor = true; 77 | // 78 | // groupBoxSearchIn 79 | // 80 | this.groupBoxSearchIn.Controls.Add(this.radioButtonText); 81 | this.groupBoxSearchIn.Controls.Add(this.radioButtonTags); 82 | this.groupBoxSearchIn.Location = new System.Drawing.Point(15, 54); 83 | this.groupBoxSearchIn.Name = "groupBoxSearchIn"; 84 | this.groupBoxSearchIn.Size = new System.Drawing.Size(223, 51); 85 | this.groupBoxSearchIn.TabIndex = 2; 86 | this.groupBoxSearchIn.TabStop = false; 87 | this.groupBoxSearchIn.Text = "Search in"; 88 | // 89 | // radioButtonText 90 | // 91 | this.radioButtonText.AutoSize = true; 92 | this.radioButtonText.Location = new System.Drawing.Point(86, 19); 93 | this.radioButtonText.Name = "radioButtonText"; 94 | this.radioButtonText.Size = new System.Drawing.Size(46, 17); 95 | this.radioButtonText.TabIndex = 1; 96 | this.radioButtonText.TabStop = true; 97 | this.radioButtonText.Text = "Text"; 98 | this.radioButtonText.UseVisualStyleBackColor = true; 99 | // 100 | // button1 101 | // 102 | this.button1.Location = new System.Drawing.Point(244, 54); 103 | this.button1.Name = "button1"; 104 | this.button1.Size = new System.Drawing.Size(98, 23); 105 | this.button1.TabIndex = 4; 106 | this.button1.Text = "C&ancel"; 107 | this.button1.UseVisualStyleBackColor = true; 108 | this.button1.Click += new System.EventHandler(this.button1_Click); 109 | // 110 | // Find 111 | // 112 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 113 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 114 | this.ClientSize = new System.Drawing.Size(354, 125); 115 | this.Controls.Add(this.button1); 116 | this.Controls.Add(this.groupBoxSearchIn); 117 | this.Controls.Add(this.textBox1); 118 | this.Controls.Add(this.label1); 119 | this.Controls.Add(this.buttonFind); 120 | this.KeyPreview = true; 121 | this.MaximizeBox = false; 122 | this.MinimizeBox = false; 123 | this.Name = "Find"; 124 | this.ShowIcon = false; 125 | this.ShowInTaskbar = false; 126 | this.Text = "Find"; 127 | this.Shown += new System.EventHandler(this.Find_Shown); 128 | this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Find_KeyDown); 129 | this.groupBoxSearchIn.ResumeLayout(false); 130 | this.groupBoxSearchIn.PerformLayout(); 131 | this.ResumeLayout(false); 132 | this.PerformLayout(); 133 | 134 | } 135 | 136 | #endregion 137 | 138 | private System.Windows.Forms.Button buttonFind; 139 | private System.Windows.Forms.Label label1; 140 | private System.Windows.Forms.TextBox textBox1; 141 | private System.Windows.Forms.RadioButton radioButtonTags; 142 | private System.Windows.Forms.GroupBox groupBoxSearchIn; 143 | private System.Windows.Forms.RadioButton radioButtonText; 144 | private System.Windows.Forms.Button button1; 145 | } 146 | } -------------------------------------------------------------------------------- /src/XmlContentTranslator/Find.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace XmlContentTranslator 5 | { 6 | public partial class Find : Form 7 | { 8 | public bool SearchTags { get; set; } 9 | public string SearchText { get; set; } 10 | 11 | public Find() 12 | { 13 | InitializeComponent(); 14 | radioButtonText.Checked = true; 15 | } 16 | 17 | private void Find_KeyDown(object sender, KeyEventArgs e) 18 | { 19 | if (e.KeyCode == Keys.Escape) 20 | { 21 | DialogResult = DialogResult.Cancel; 22 | } 23 | else if (e.KeyCode == Keys.Enter) 24 | { 25 | buttonFind_Click(sender, e); 26 | } 27 | } 28 | 29 | private void button1_Click(object sender, EventArgs e) 30 | { 31 | DialogResult = DialogResult.Cancel; 32 | } 33 | 34 | private void buttonFind_Click(object sender, EventArgs e) 35 | { 36 | SearchTags = radioButtonTags.Checked; 37 | SearchText = textBox1.Text; 38 | DialogResult = DialogResult.OK; 39 | } 40 | 41 | private void Find_Shown(object sender, EventArgs e) 42 | { 43 | textBox1.Focus(); 44 | textBox1.SelectAll(); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Find.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/ListViewItemComparer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Windows.Forms; 5 | 6 | namespace XmlContentTranslator 7 | { 8 | public class ListViewItemComparer : IComparer 9 | { 10 | public static Dictionary NoSortOrder = new Dictionary(); 11 | 12 | public int ColumnIndex { get; set; } 13 | 14 | public SortOrder SortDirection { get; set; } 15 | 16 | public TypeCode ColumnType { get; set; } 17 | 18 | public ListViewItemComparer() 19 | { 20 | SortDirection = SortOrder.None; 21 | } 22 | 23 | public int Compare(object x, object y) 24 | { 25 | var lviX = x as ListViewItem; 26 | var lviY = y as ListViewItem; 27 | 28 | if (SortDirection == SortOrder.None) 29 | { 30 | return decimal.Compare(NoSortOrder[lviX], NoSortOrder[lviY]); 31 | } 32 | 33 | if (lviX == null && lviY == null) 34 | { 35 | return CompareResult(0); 36 | } 37 | 38 | if (lviX == null) 39 | { 40 | return CompareResult(-1); ; 41 | } 42 | 43 | if (lviY == null) 44 | { 45 | return CompareResult(1); 46 | } 47 | 48 | switch (ColumnType) 49 | { 50 | case TypeCode.Decimal: 51 | decimal xD = 0; 52 | if (decimal.TryParse(lviX.SubItems[ColumnIndex].Text, out var dx)) 53 | { 54 | xD = dx; 55 | } 56 | 57 | decimal yD = 0; 58 | if (decimal.TryParse(lviY.SubItems[ColumnIndex].Text, out var dy)) 59 | { 60 | yD = dy; 61 | } 62 | 63 | return CompareResult(decimal.Compare(xD, yD)); 64 | 65 | default: 66 | return CompareResult(string.Compare( 67 | lviX.SubItems[ColumnIndex].Text, 68 | lviY.SubItems[ColumnIndex].Text)); 69 | } 70 | } 71 | 72 | private int CompareResult(int result) 73 | { 74 | if (SortDirection == SortOrder.Descending) 75 | { 76 | return -result; 77 | } 78 | 79 | return result; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Main.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace XmlContentTranslator 2 | { 3 | partial class Main 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.components = new System.ComponentModel.Container(); 32 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Main)); 33 | this.listViewLanguageTags = new System.Windows.Forms.ListView(); 34 | this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); 35 | this.translateViaGoogleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 36 | this.setValueFromMasterToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 37 | this.menuStrip1 = new System.Windows.Forms.MenuStrip(); 38 | this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 39 | this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); 40 | this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 41 | this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripMenuItem(); 42 | this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 43 | this.saveAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 44 | this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); 45 | this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 46 | this.editToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 47 | this.findToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 48 | this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 49 | this.googleTranslateSelectedLinesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 50 | this.textBoxCurrentText = new System.Windows.Forms.TextBox(); 51 | this.treeView1 = new System.Windows.Forms.TreeView(); 52 | this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog(); 53 | this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog(); 54 | this.statusStrip1 = new System.Windows.Forms.StatusStrip(); 55 | this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel(); 56 | this.toolStripStatusLabel2 = new System.Windows.Forms.ToolStripStatusLabel(); 57 | this.labelTo = new System.Windows.Forms.Label(); 58 | this.comboBoxTo = new System.Windows.Forms.ComboBox(); 59 | this.labelFrom = new System.Windows.Forms.Label(); 60 | this.comboBoxFrom = new System.Windows.Forms.ComboBox(); 61 | this.buttonGoToNextBlankLine = new System.Windows.Forms.Button(); 62 | this.contextMenuStrip1.SuspendLayout(); 63 | this.menuStrip1.SuspendLayout(); 64 | this.statusStrip1.SuspendLayout(); 65 | this.SuspendLayout(); 66 | // 67 | // listViewLanguageTags 68 | // 69 | this.listViewLanguageTags.AllowDrop = true; 70 | this.listViewLanguageTags.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 71 | | System.Windows.Forms.AnchorStyles.Left) 72 | | System.Windows.Forms.AnchorStyles.Right))); 73 | this.listViewLanguageTags.ContextMenuStrip = this.contextMenuStrip1; 74 | this.listViewLanguageTags.FullRowSelect = true; 75 | this.listViewLanguageTags.HideSelection = false; 76 | this.listViewLanguageTags.Location = new System.Drawing.Point(255, 60); 77 | this.listViewLanguageTags.Name = "listViewLanguageTags"; 78 | this.listViewLanguageTags.Size = new System.Drawing.Size(652, 421); 79 | this.listViewLanguageTags.TabIndex = 10; 80 | this.listViewLanguageTags.UseCompatibleStateImageBehavior = false; 81 | this.listViewLanguageTags.View = System.Windows.Forms.View.Details; 82 | this.listViewLanguageTags.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.listViewLanguageTags_ColumnClick); 83 | this.listViewLanguageTags.SelectedIndexChanged += new System.EventHandler(this.ListViewLanguageTagsSelectedIndexChanged); 84 | this.listViewLanguageTags.DragDrop += new System.Windows.Forms.DragEventHandler(this.ListViewLanguageTagsDragDrop); 85 | this.listViewLanguageTags.DragEnter += new System.Windows.Forms.DragEventHandler(this.ListViewLanguageTagsDragEnter); 86 | this.listViewLanguageTags.DoubleClick += new System.EventHandler(this.listViewLanguageTags_DoubleClick); 87 | // 88 | // contextMenuStrip1 89 | // 90 | this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { 91 | this.translateViaGoogleToolStripMenuItem, 92 | this.setValueFromMasterToolStripMenuItem}); 93 | this.contextMenuStrip1.Name = "contextMenuStrip1"; 94 | this.contextMenuStrip1.Size = new System.Drawing.Size(215, 48); 95 | // 96 | // translateViaGoogleToolStripMenuItem 97 | // 98 | this.translateViaGoogleToolStripMenuItem.Name = "translateViaGoogleToolStripMenuItem"; 99 | this.translateViaGoogleToolStripMenuItem.Size = new System.Drawing.Size(214, 22); 100 | this.translateViaGoogleToolStripMenuItem.Text = "Translate via google"; 101 | this.translateViaGoogleToolStripMenuItem.Click += new System.EventHandler(this.translateViaGoogleToolStripMenuItem_Click); 102 | // 103 | // setValueFromMasterToolStripMenuItem 104 | // 105 | this.setValueFromMasterToolStripMenuItem.Name = "setValueFromMasterToolStripMenuItem"; 106 | this.setValueFromMasterToolStripMenuItem.Size = new System.Drawing.Size(214, 22); 107 | this.setValueFromMasterToolStripMenuItem.Text = "Transfer value from master"; 108 | this.setValueFromMasterToolStripMenuItem.Click += new System.EventHandler(this.setValueFromMasterToolStripMenuItem_Click); 109 | // 110 | // menuStrip1 111 | // 112 | this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { 113 | this.fileToolStripMenuItem, 114 | this.editToolStripMenuItem, 115 | this.toolsToolStripMenuItem}); 116 | this.menuStrip1.Location = new System.Drawing.Point(0, 0); 117 | this.menuStrip1.Name = "menuStrip1"; 118 | this.menuStrip1.Size = new System.Drawing.Size(919, 24); 119 | this.menuStrip1.TabIndex = 11; 120 | this.menuStrip1.Text = "menuStrip1"; 121 | // 122 | // fileToolStripMenuItem 123 | // 124 | this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { 125 | this.toolStripMenuItem1, 126 | this.openToolStripMenuItem, 127 | this.toolStripMenuItem2, 128 | this.saveToolStripMenuItem, 129 | this.saveAsToolStripMenuItem, 130 | this.toolStripSeparator1, 131 | this.exitToolStripMenuItem}); 132 | this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; 133 | this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); 134 | this.fileToolStripMenuItem.Text = "&File"; 135 | // 136 | // toolStripMenuItem1 137 | // 138 | this.toolStripMenuItem1.Name = "toolStripMenuItem1"; 139 | this.toolStripMenuItem1.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.N))); 140 | this.toolStripMenuItem1.Size = new System.Drawing.Size(341, 22); 141 | this.toolStripMenuItem1.Text = "&New"; 142 | this.toolStripMenuItem1.Click += new System.EventHandler(this.ToolStripMenuItem1Click); 143 | // 144 | // openToolStripMenuItem 145 | // 146 | this.openToolStripMenuItem.Name = "openToolStripMenuItem"; 147 | this.openToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O))); 148 | this.openToolStripMenuItem.Size = new System.Drawing.Size(341, 22); 149 | this.openToolStripMenuItem.Text = "&Open"; 150 | this.openToolStripMenuItem.Click += new System.EventHandler(this.OpenToolStripMenuItemClick); 151 | // 152 | // toolStripMenuItem2 153 | // 154 | this.toolStripMenuItem2.Name = "toolStripMenuItem2"; 155 | this.toolStripMenuItem2.Size = new System.Drawing.Size(341, 22); 156 | this.toolStripMenuItem2.Text = "Open Subtitle Edit English translation base online..."; 157 | this.toolStripMenuItem2.Click += new System.EventHandler(this.toolStripMenuItem2_Click); 158 | // 159 | // saveToolStripMenuItem 160 | // 161 | this.saveToolStripMenuItem.Name = "saveToolStripMenuItem"; 162 | this.saveToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S))); 163 | this.saveToolStripMenuItem.Size = new System.Drawing.Size(341, 22); 164 | this.saveToolStripMenuItem.Text = "&Save"; 165 | this.saveToolStripMenuItem.Click += new System.EventHandler(this.SaveToolStripMenuItemClick); 166 | // 167 | // saveAsToolStripMenuItem 168 | // 169 | this.saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem"; 170 | this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(341, 22); 171 | this.saveAsToolStripMenuItem.Text = "Save &as..."; 172 | this.saveAsToolStripMenuItem.Click += new System.EventHandler(this.SaveAsToolStripMenuItemClick); 173 | // 174 | // toolStripSeparator1 175 | // 176 | this.toolStripSeparator1.Name = "toolStripSeparator1"; 177 | this.toolStripSeparator1.Size = new System.Drawing.Size(338, 6); 178 | // 179 | // exitToolStripMenuItem 180 | // 181 | this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; 182 | this.exitToolStripMenuItem.Size = new System.Drawing.Size(341, 22); 183 | this.exitToolStripMenuItem.Text = "E&xit"; 184 | this.exitToolStripMenuItem.Click += new System.EventHandler(this.ExitToolStripMenuItemClick); 185 | // 186 | // editToolStripMenuItem 187 | // 188 | this.editToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { 189 | this.findToolStripMenuItem}); 190 | this.editToolStripMenuItem.Name = "editToolStripMenuItem"; 191 | this.editToolStripMenuItem.Size = new System.Drawing.Size(39, 20); 192 | this.editToolStripMenuItem.Text = "&Edit"; 193 | // 194 | // findToolStripMenuItem 195 | // 196 | this.findToolStripMenuItem.Name = "findToolStripMenuItem"; 197 | this.findToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.F))); 198 | this.findToolStripMenuItem.Size = new System.Drawing.Size(137, 22); 199 | this.findToolStripMenuItem.Text = "&Find"; 200 | this.findToolStripMenuItem.Click += new System.EventHandler(this.findToolStripMenuItem_Click); 201 | // 202 | // toolsToolStripMenuItem 203 | // 204 | this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { 205 | this.googleTranslateSelectedLinesToolStripMenuItem}); 206 | this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem"; 207 | this.toolsToolStripMenuItem.Size = new System.Drawing.Size(46, 20); 208 | this.toolsToolStripMenuItem.Text = "&Tools"; 209 | // 210 | // googleTranslateSelectedLinesToolStripMenuItem 211 | // 212 | this.googleTranslateSelectedLinesToolStripMenuItem.Name = "googleTranslateSelectedLinesToolStripMenuItem"; 213 | this.googleTranslateSelectedLinesToolStripMenuItem.Size = new System.Drawing.Size(233, 22); 214 | this.googleTranslateSelectedLinesToolStripMenuItem.Text = "Google &translate selected lines"; 215 | this.googleTranslateSelectedLinesToolStripMenuItem.Click += new System.EventHandler(this.GoogleTranslateSelectedLinesToolStripMenuItemClick); 216 | // 217 | // textBoxCurrentText 218 | // 219 | this.textBoxCurrentText.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) 220 | | System.Windows.Forms.AnchorStyles.Right))); 221 | this.textBoxCurrentText.Location = new System.Drawing.Point(255, 487); 222 | this.textBoxCurrentText.Multiline = true; 223 | this.textBoxCurrentText.Name = "textBoxCurrentText"; 224 | this.textBoxCurrentText.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; 225 | this.textBoxCurrentText.Size = new System.Drawing.Size(652, 80); 226 | this.textBoxCurrentText.TabIndex = 12; 227 | this.textBoxCurrentText.TextChanged += new System.EventHandler(this.TextBoxCurrentTextTextChanged); 228 | this.textBoxCurrentText.KeyDown += new System.Windows.Forms.KeyEventHandler(this.TextBoxCurrentTextKeyDown); 229 | // 230 | // treeView1 231 | // 232 | this.treeView1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 233 | | System.Windows.Forms.AnchorStyles.Left))); 234 | this.treeView1.HideSelection = false; 235 | this.treeView1.Location = new System.Drawing.Point(13, 60); 236 | this.treeView1.Name = "treeView1"; 237 | this.treeView1.Size = new System.Drawing.Size(236, 507); 238 | this.treeView1.TabIndex = 13; 239 | this.treeView1.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(this.TreeView1AfterSelect); 240 | // 241 | // openFileDialog1 242 | // 243 | this.openFileDialog1.FileName = "openFileDialog1"; 244 | // 245 | // statusStrip1 246 | // 247 | this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { 248 | this.toolStripStatusLabel1, 249 | this.toolStripStatusLabel2}); 250 | this.statusStrip1.Location = new System.Drawing.Point(0, 570); 251 | this.statusStrip1.Name = "statusStrip1"; 252 | this.statusStrip1.Size = new System.Drawing.Size(919, 22); 253 | this.statusStrip1.TabIndex = 14; 254 | this.statusStrip1.Text = "statusStrip1"; 255 | // 256 | // toolStripStatusLabel1 257 | // 258 | this.toolStripStatusLabel1.Name = "toolStripStatusLabel1"; 259 | this.toolStripStatusLabel1.Size = new System.Drawing.Size(118, 17); 260 | this.toolStripStatusLabel1.Text = "toolStripStatusLabel1"; 261 | // 262 | // toolStripStatusLabel2 263 | // 264 | this.toolStripStatusLabel2.Name = "toolStripStatusLabel2"; 265 | this.toolStripStatusLabel2.Size = new System.Drawing.Size(786, 17); 266 | this.toolStripStatusLabel2.Spring = true; 267 | this.toolStripStatusLabel2.Text = "toolStripStatusLabel2"; 268 | this.toolStripStatusLabel2.TextAlign = System.Drawing.ContentAlignment.MiddleRight; 269 | // 270 | // labelTo 271 | // 272 | this.labelTo.AutoSize = true; 273 | this.labelTo.Location = new System.Drawing.Point(639, 36); 274 | this.labelTo.Name = "labelTo"; 275 | this.labelTo.Size = new System.Drawing.Size(23, 13); 276 | this.labelTo.TabIndex = 19; 277 | this.labelTo.Text = "To:"; 278 | // 279 | // comboBoxTo 280 | // 281 | this.comboBoxTo.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 282 | this.comboBoxTo.FormattingEnabled = true; 283 | this.comboBoxTo.Location = new System.Drawing.Point(665, 33); 284 | this.comboBoxTo.Name = "comboBoxTo"; 285 | this.comboBoxTo.Size = new System.Drawing.Size(121, 21); 286 | this.comboBoxTo.TabIndex = 18; 287 | // 288 | // labelFrom 289 | // 290 | this.labelFrom.AutoSize = true; 291 | this.labelFrom.Location = new System.Drawing.Point(450, 36); 292 | this.labelFrom.Name = "labelFrom"; 293 | this.labelFrom.Size = new System.Drawing.Size(33, 13); 294 | this.labelFrom.TabIndex = 17; 295 | this.labelFrom.Text = "From:"; 296 | // 297 | // comboBoxFrom 298 | // 299 | this.comboBoxFrom.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 300 | this.comboBoxFrom.FormattingEnabled = true; 301 | this.comboBoxFrom.Location = new System.Drawing.Point(486, 33); 302 | this.comboBoxFrom.Name = "comboBoxFrom"; 303 | this.comboBoxFrom.Size = new System.Drawing.Size(121, 21); 304 | this.comboBoxFrom.TabIndex = 16; 305 | // 306 | // buttonGoToNextBlankLine 307 | // 308 | this.buttonGoToNextBlankLine.Location = new System.Drawing.Point(256, 31); 309 | this.buttonGoToNextBlankLine.Name = "buttonGoToNextBlankLine"; 310 | this.buttonGoToNextBlankLine.Size = new System.Drawing.Size(156, 23); 311 | this.buttonGoToNextBlankLine.TabIndex = 20; 312 | this.buttonGoToNextBlankLine.Text = "Go to next blank line (F6)"; 313 | this.buttonGoToNextBlankLine.UseVisualStyleBackColor = true; 314 | this.buttonGoToNextBlankLine.Click += new System.EventHandler(this.ButtonGoToNextBlankLineClick); 315 | // 316 | // Main 317 | // 318 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 319 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 320 | this.ClientSize = new System.Drawing.Size(919, 592); 321 | this.Controls.Add(this.buttonGoToNextBlankLine); 322 | this.Controls.Add(this.labelTo); 323 | this.Controls.Add(this.comboBoxTo); 324 | this.Controls.Add(this.labelFrom); 325 | this.Controls.Add(this.comboBoxFrom); 326 | this.Controls.Add(this.statusStrip1); 327 | this.Controls.Add(this.treeView1); 328 | this.Controls.Add(this.textBoxCurrentText); 329 | this.Controls.Add(this.listViewLanguageTags); 330 | this.Controls.Add(this.menuStrip1); 331 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); 332 | this.KeyPreview = true; 333 | this.MainMenuStrip = this.menuStrip1; 334 | this.MinimumSize = new System.Drawing.Size(820, 350); 335 | this.Name = "Main"; 336 | this.Text = "XML Content Translator 1.12"; 337 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Form1FormClosing); 338 | this.Load += new System.EventHandler(this.Form1Load); 339 | this.ResizeEnd += new System.EventHandler(this.Main_ResizeEnd); 340 | this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1KeyDown); 341 | this.Resize += new System.EventHandler(this.Main_Resize); 342 | this.contextMenuStrip1.ResumeLayout(false); 343 | this.menuStrip1.ResumeLayout(false); 344 | this.menuStrip1.PerformLayout(); 345 | this.statusStrip1.ResumeLayout(false); 346 | this.statusStrip1.PerformLayout(); 347 | this.ResumeLayout(false); 348 | this.PerformLayout(); 349 | 350 | } 351 | 352 | #endregion 353 | 354 | private System.Windows.Forms.ListView listViewLanguageTags; 355 | private System.Windows.Forms.MenuStrip menuStrip1; 356 | private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; 357 | private System.Windows.Forms.ToolStripMenuItem openToolStripMenuItem; 358 | private System.Windows.Forms.TextBox textBoxCurrentText; 359 | private System.Windows.Forms.TreeView treeView1; 360 | private System.Windows.Forms.ToolStripMenuItem saveToolStripMenuItem; 361 | private System.Windows.Forms.ToolStripMenuItem saveAsToolStripMenuItem; 362 | private System.Windows.Forms.ToolStripMenuItem toolsToolStripMenuItem; 363 | private System.Windows.Forms.ToolStripMenuItem googleTranslateSelectedLinesToolStripMenuItem; 364 | private System.Windows.Forms.OpenFileDialog openFileDialog1; 365 | private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem1; 366 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; 367 | private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem; 368 | private System.Windows.Forms.SaveFileDialog saveFileDialog1; 369 | private System.Windows.Forms.StatusStrip statusStrip1; 370 | private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1; 371 | private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel2; 372 | private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; 373 | private System.Windows.Forms.ToolStripMenuItem setValueFromMasterToolStripMenuItem; 374 | private System.Windows.Forms.ToolStripMenuItem translateViaGoogleToolStripMenuItem; 375 | private System.Windows.Forms.Label labelTo; 376 | private System.Windows.Forms.ComboBox comboBoxTo; 377 | private System.Windows.Forms.Label labelFrom; 378 | private System.Windows.Forms.ComboBox comboBoxFrom; 379 | private System.Windows.Forms.Button buttonGoToNextBlankLine; 380 | private System.Windows.Forms.ToolStripMenuItem toolStripMenuItem2; 381 | private System.Windows.Forms.ToolStripMenuItem editToolStripMenuItem; 382 | private System.Windows.Forms.ToolStripMenuItem findToolStripMenuItem; 383 | } 384 | } 385 | 386 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Main.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Drawing; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Net; 8 | using System.Text; 9 | using System.Web; 10 | using System.Windows.Forms; 11 | using System.Xml; 12 | using XmlContentTranslator.Translator; 13 | 14 | namespace XmlContentTranslator 15 | { 16 | public partial class Main : Form 17 | { 18 | public class ComboBoxItem 19 | { 20 | private string Text { get; } 21 | public string Value { get; } 22 | 23 | public ComboBoxItem(string text, string value) 24 | { 25 | if (text.Length > 1) 26 | { 27 | text = text.Substring(0, 1).ToUpper() + text.Substring(1).ToLower(); 28 | } 29 | 30 | Text = text; 31 | Value = value; 32 | } 33 | 34 | public override string ToString() 35 | { 36 | return Text; 37 | } 38 | } 39 | 40 | private Hashtable _treeNodesHashtable = new Hashtable(); 41 | private Hashtable _listViewItemHashtable = new Hashtable(); 42 | private XmlDocument _originalDocument; 43 | private string _secondLanguageFileName; 44 | private bool _change; 45 | private Find _formFind; 46 | 47 | public Main() 48 | { 49 | InitializeComponent(); 50 | toolStripStatusLabel1.Text = string.Empty; 51 | toolStripStatusLabel2.Text = string.Empty; 52 | 53 | FillComboWithLanguages(comboBoxFrom); 54 | FillComboWithLanguages(comboBoxTo); 55 | } 56 | 57 | private void listViewLanguageTags_ColumnClick(object sender, ColumnClickEventArgs e) 58 | { 59 | var sorter = GetListViewSorter(e.Column); 60 | listViewLanguageTags.ListViewItemSorter = sorter; 61 | listViewLanguageTags.Sort(); 62 | } 63 | 64 | private ListViewItemComparer GetListViewSorter(int columnIndex) 65 | { 66 | var sorter = (ListViewItemComparer)listViewLanguageTags.ListViewItemSorter; 67 | if (sorter == null) 68 | { 69 | sorter = new ListViewItemComparer(); 70 | } 71 | 72 | sorter.ColumnIndex = columnIndex; 73 | 74 | var columnName = listViewLanguageTags.Columns[columnIndex].Name; 75 | switch (columnName) 76 | { 77 | default: 78 | sorter.ColumnType = TypeCode.String; 79 | break; 80 | } 81 | 82 | for (var i = 0; i < listViewLanguageTags.Columns.Count; i++) 83 | { 84 | listViewLanguageTags.Columns[i].Text = listViewLanguageTags.Columns[i].Text.TrimEnd(' ', '↑', '↓'); 85 | } 86 | 87 | if (sorter.SortDirection == SortOrder.Ascending) 88 | { 89 | sorter.SortDirection = SortOrder.Descending; 90 | listViewLanguageTags.Columns[columnIndex].Text += " ↑"; 91 | } 92 | else if (sorter.SortDirection == SortOrder.Descending) 93 | { 94 | sorter.SortDirection = SortOrder.None; 95 | } 96 | else 97 | { 98 | sorter.SortDirection = SortOrder.Ascending; 99 | listViewLanguageTags.Columns[columnIndex].Text += " ↓"; 100 | } 101 | 102 | return sorter; 103 | } 104 | 105 | private void OpenToolStripMenuItemClick(object sender, EventArgs e) 106 | { 107 | if (_change && listViewLanguageTags.Columns.Count == 3 && 108 | MessageBox.Show("Changes will be lost. Continue?", "Continue", MessageBoxButtons.YesNo) == DialogResult.No) 109 | { 110 | return; 111 | } 112 | 113 | openFileDialog1.FileName = string.Empty; 114 | openFileDialog1.DefaultExt = ".xml"; 115 | openFileDialog1.Filter = "Xml files|*.xml" + "|All files|*.*"; 116 | openFileDialog1.Title = "Open language master file"; 117 | if (openFileDialog1.ShowDialog() == DialogResult.OK) 118 | { 119 | MakeNew(); 120 | if (OpenFirstFile(openFileDialog1.FileName)) 121 | { 122 | OpenSecondFile(); 123 | } 124 | } 125 | } 126 | 127 | private bool OpenFirstFile(string fileName) 128 | { 129 | toolStripStatusLabel1.Text = "Opening " + fileName + "..."; 130 | var doc = new XmlDocument(); 131 | try 132 | { 133 | doc.Load(fileName); 134 | } 135 | catch 136 | { 137 | MessageBox.Show("Not a valid xml file: " + fileName); 138 | return false; 139 | } 140 | 141 | return OpenFirstXmlDocument(doc); 142 | } 143 | 144 | private bool OpenFirstXmlDocument(XmlDocument doc) 145 | { 146 | listViewLanguageTags.Columns.Add("Tag", 150); 147 | TryGetLanguageNameAttribute(doc, comboBoxFrom); 148 | 149 | AddAttributes(doc.DocumentElement); 150 | if (doc.DocumentElement != null) 151 | { 152 | foreach (XmlNode childNode in doc.DocumentElement.ChildNodes) 153 | { 154 | if (childNode.NodeType != XmlNodeType.Attribute) 155 | { 156 | var treeNode = new TreeNode(childNode.Name); 157 | treeNode.Tag = childNode; 158 | treeView1.Nodes.Add(treeNode); 159 | if (childNode.ChildNodes.Count > 0 && !XmlUtils.IsTextNode(childNode)) 160 | { 161 | ExpandNode(treeNode, childNode); 162 | } 163 | else 164 | { 165 | _treeNodesHashtable.Add(treeNode, childNode); 166 | AddListViewItem(childNode); 167 | } 168 | } 169 | } 170 | } 171 | _originalDocument = doc; 172 | toolStripStatusLabel1.Text = "Done reading " + openFileDialog1.FileName; 173 | return true; 174 | } 175 | 176 | private void SetLanguage(ComboBox comboBox, XmlDocument doc) 177 | { 178 | int index = 0; 179 | comboBox.SelectedIndex = -1; 180 | int defaultIndex = -1; 181 | bool isDefaultSet = false; 182 | if (doc != null && doc.DocumentElement != null && doc.DocumentElement.SelectSingleNode("General/CultureName") != null) 183 | { 184 | string culture = doc.DocumentElement.SelectSingleNode("General/CultureName").InnerText; 185 | foreach (ComboBoxItem item in comboBox.Items) 186 | { 187 | if (item.Value == culture) 188 | { 189 | comboBox.SelectedIndex = index; 190 | return; 191 | } 192 | if (isDefaultSet == false && item.Value == "en") 193 | { 194 | defaultIndex = index; 195 | isDefaultSet = true; 196 | } 197 | index++; 198 | } 199 | 200 | culture = culture.Substring(0, 2); 201 | index = 0; 202 | foreach (ComboBoxItem item in comboBox.Items) 203 | { 204 | if (item.Value == culture) 205 | { 206 | comboBox.SelectedIndex = index; 207 | return; 208 | } 209 | index++; 210 | } 211 | } 212 | if (defaultIndex >= 0) 213 | comboBox.SelectedIndex = defaultIndex; 214 | } 215 | 216 | private void OpenSecondFile() 217 | { 218 | _secondLanguageFileName = string.Empty; 219 | Text = "XML Content Translator - New"; 220 | openFileDialog1.Title = "Open file to translate/correct"; 221 | if (openFileDialog1.ShowDialog() == DialogResult.OK) 222 | { 223 | OpenSecondFile(openFileDialog1.FileName); 224 | } 225 | else 226 | { 227 | listViewLanguageTags.Columns.Add("Language 2", 200); 228 | SetLanguage(comboBoxTo, null); 229 | CreateEmptyLanguage(); 230 | } 231 | HighLightLinesWithSameText(); 232 | } 233 | 234 | private void OpenSecondFile(string fileName) 235 | { 236 | toolStripStatusLabel1.Text = "Opening " + fileName + "..."; 237 | _secondLanguageFileName = fileName; 238 | Text = "XML Content Translator - " + _secondLanguageFileName; 239 | 240 | Cursor = Cursors.WaitCursor; 241 | listViewLanguageTags.BeginUpdate(); 242 | var doc = new XmlDocument(); 243 | try 244 | { 245 | doc.Load(_secondLanguageFileName); 246 | } 247 | catch 248 | { 249 | MessageBox.Show("Not a valid xml file: " + _secondLanguageFileName); 250 | } 251 | 252 | TryGetLanguageNameAttribute(doc, comboBoxTo); 253 | 254 | AddAttributes(doc.DocumentElement); 255 | if (doc.DocumentElement != null) 256 | { 257 | foreach (XmlNode childNode in doc.DocumentElement.ChildNodes) 258 | { 259 | if (childNode.ChildNodes.Count > 0 && !XmlUtils.IsTextNode(childNode)) 260 | { 261 | ExpandNode(null, childNode); 262 | } 263 | else 264 | { 265 | AddListViewItem(childNode); 266 | AddAttributes(doc.DocumentElement); 267 | } 268 | } 269 | } 270 | 271 | CreateEmptyLanguage(); 272 | 273 | listViewLanguageTags.EndUpdate(); 274 | Cursor = Cursors.Default; 275 | toolStripStatusLabel1.Text = "Done reading " + _secondLanguageFileName; 276 | } 277 | 278 | private void TryGetLanguageNameAttribute(XmlDocument doc, ComboBox cb) 279 | { 280 | if (doc.DocumentElement != null && doc.DocumentElement.Attributes["Name"] != null) 281 | { 282 | listViewLanguageTags.Columns.Add(doc.DocumentElement.Attributes["Name"].InnerText, 200); 283 | } 284 | else if (doc.DocumentElement != null && doc.DocumentElement.Attributes["name"] != null) 285 | { 286 | listViewLanguageTags.Columns.Add(doc.DocumentElement.Attributes["name"].InnerText, 200); 287 | } 288 | else 289 | { 290 | string language = "Language1"; 291 | if (cb.Name == "comboBoxTo") 292 | { 293 | language = "Language2"; 294 | } 295 | listViewLanguageTags.Columns.Add(language, 200); 296 | } 297 | SetLanguage(cb, doc); 298 | } 299 | 300 | private void CreateEmptyLanguage() 301 | { 302 | foreach (ListViewItem lvi in listViewLanguageTags.Items) 303 | { 304 | if (lvi.SubItems.Count == 2) 305 | { 306 | lvi.SubItems.Add(string.Empty); 307 | } 308 | } 309 | } 310 | 311 | private void AddListViewItem(XmlNode node) 312 | { 313 | if (listViewLanguageTags.Columns.Count == 2) 314 | { 315 | if (node.NodeType == XmlNodeType.Comment || node.NodeType == XmlNodeType.CDATA) 316 | { 317 | return; 318 | } 319 | 320 | ListViewItem item; 321 | if (node.NodeType == XmlNodeType.Attribute) 322 | { 323 | item = new ListViewItem("@" + node.Name); 324 | item.SubItems.Add(node.InnerText); 325 | } 326 | else if (XmlUtils.ContainsText(node)) 327 | { 328 | item = new ListViewItem(node.Name); 329 | item.SubItems.Add(node.InnerXml); 330 | } 331 | else 332 | { 333 | item = new ListViewItem(node.Name); 334 | item.SubItems.Add(node.InnerText); 335 | } 336 | 337 | 338 | item.Tag = node; 339 | listViewLanguageTags.Items.Add(item); 340 | ListViewItemComparer.NoSortOrder.Add(item, listViewLanguageTags.Items.Count); 341 | 342 | _listViewItemHashtable.Add(XmlUtils.BuildNodePath(node), item); // fails on some attributes!! 343 | } 344 | else if (listViewLanguageTags.Columns.Count == 3) 345 | { 346 | if (_listViewItemHashtable[XmlUtils.BuildNodePath(node)] is ListViewItem item && item.SubItems.Count < 4096) 347 | { 348 | item.SubItems.Add(XmlUtils.ContainsText(node) ? node.InnerXml : node.InnerText); 349 | } 350 | } 351 | } 352 | 353 | private void MakeNew() 354 | { 355 | _treeNodesHashtable = new Hashtable(); 356 | _listViewItemHashtable = new Hashtable(); 357 | treeView1.Nodes.Clear(); 358 | listViewLanguageTags.Items.Clear(); 359 | listViewLanguageTags.Clear(); 360 | 361 | _secondLanguageFileName = string.Empty; 362 | Text = "XML Content Translator"; 363 | 364 | _change = false; 365 | } 366 | 367 | private void ExpandNode(TreeNode parentNode, XmlNode node) 368 | { 369 | if (listViewLanguageTags.Columns.Count == 2) 370 | { 371 | AddAttributes(node); 372 | foreach (XmlNode childNode in node.ChildNodes) 373 | { 374 | var treeNode = new TreeNode(childNode.Name); 375 | treeNode.Tag = childNode; 376 | if (parentNode == null) 377 | { 378 | treeView1.Nodes.Add(treeNode); 379 | } 380 | else 381 | { 382 | parentNode.Nodes.Add(treeNode); 383 | } 384 | 385 | if (XmlUtils.IsParentElement(childNode)) 386 | { 387 | ExpandNode(treeNode, childNode); 388 | } 389 | else 390 | { 391 | _treeNodesHashtable.Add(treeNode, childNode); 392 | AddListViewItem(childNode); 393 | AddAttributes(childNode); 394 | } 395 | } 396 | } 397 | else if (listViewLanguageTags.Columns.Count == 3) 398 | { 399 | AddAttributes(node); 400 | foreach (XmlNode childNode in node.ChildNodes) 401 | { 402 | if (XmlUtils.IsParentElement(childNode)) 403 | { 404 | ExpandNode(null, childNode); 405 | } 406 | else 407 | { 408 | AddListViewItem(childNode); 409 | AddAttributes(childNode); 410 | } 411 | } 412 | } 413 | } 414 | 415 | private void AddAttributes(XmlNode node) 416 | { 417 | if (node.Attributes == null || node.Attributes.Count == 0) 418 | { 419 | return; 420 | } 421 | 422 | foreach (XmlNode childNode in node.Attributes) 423 | { 424 | AddListViewItem(childNode); 425 | } 426 | } 427 | 428 | private void ExitToolStripMenuItemClick(object sender, EventArgs e) 429 | { 430 | if (_change && listViewLanguageTags.Columns.Count == 3 && 431 | MessageBox.Show("Changes will be lost. Continue?", "Continue", MessageBoxButtons.YesNo) == DialogResult.No) 432 | { 433 | return; 434 | } 435 | 436 | _change = false; 437 | Close(); 438 | } 439 | 440 | private void TreeView1AfterSelect(object sender, TreeViewEventArgs e) 441 | { 442 | var node = _treeNodesHashtable[e.Node] as XmlNode; 443 | if (node != null) 444 | { 445 | DeSelectListViewItems(); 446 | 447 | var item = _listViewItemHashtable[XmlUtils.BuildNodePath(node)] as ListViewItem; 448 | if (item != null) 449 | { 450 | item.Selected = true; 451 | listViewLanguageTags.EnsureVisible(item.Index); 452 | } 453 | } 454 | } 455 | 456 | private void DeSelectListViewItems() 457 | { 458 | var selectedItems = new List(); 459 | foreach (ListViewItem lvi in listViewLanguageTags.SelectedItems) 460 | { 461 | selectedItems.Add(lvi); 462 | } 463 | foreach (ListViewItem lvi in selectedItems) 464 | { 465 | lvi.Selected = false; 466 | } 467 | } 468 | 469 | private void ListViewLanguageTagsSelectedIndexChanged(object sender, EventArgs e) 470 | { 471 | if (listViewLanguageTags.SelectedItems.Count == 1 && listViewLanguageTags.SelectedItems[0].SubItems.Count > 2) 472 | { 473 | textBoxCurrentText.Enabled = true; 474 | textBoxCurrentText.Text = listViewLanguageTags.SelectedItems[0].SubItems[2].Text; 475 | 476 | if (listViewLanguageTags.SelectedItems[0].Tag is XmlNode node) 477 | { 478 | toolStripStatusLabel2.Text = $"{XmlUtils.BuildNodePath(node).Replace("#document/", "")} {listViewLanguageTags.SelectedItems[0].Index + 1} / {listViewLanguageTags.Items.Count}"; 479 | } 480 | else 481 | { 482 | toolStripStatusLabel2.Text = $"{listViewLanguageTags.SelectedItems[0].Index + 1} / {listViewLanguageTags.Items.Count}"; 483 | } 484 | } 485 | else 486 | { 487 | textBoxCurrentText.Text = string.Empty; 488 | textBoxCurrentText.Enabled = false; 489 | toolStripStatusLabel2.Text = $"{listViewLanguageTags.SelectedItems.Count} items selected"; 490 | } 491 | HighLightLinesWithSameText(); 492 | } 493 | 494 | private void TextBoxCurrentTextTextChanged(object sender, EventArgs e) 495 | { 496 | if (listViewLanguageTags.SelectedItems.Count == 1) 497 | { 498 | listViewLanguageTags.SelectedItems[0].SubItems[2].Text = textBoxCurrentText.Text; 499 | } 500 | } 501 | 502 | private void FillOriginalDocumentFromSecondLanguage() 503 | { 504 | FillAttributes(_originalDocument.DocumentElement); 505 | if (_originalDocument.DocumentElement != null) 506 | { 507 | foreach (XmlNode childNode in _originalDocument.DocumentElement.ChildNodes) 508 | { 509 | if (childNode.ChildNodes.Count > 0 && !XmlUtils.IsTextNode(childNode)) 510 | { 511 | FillOriginalDocumentExpandNode(childNode); 512 | } 513 | else 514 | { 515 | var item = _listViewItemHashtable[XmlUtils.BuildNodePath(childNode)] as ListViewItem; 516 | if (item != null) 517 | { 518 | childNode.InnerText = item.SubItems[2].Text; 519 | } 520 | FillAttributes(_originalDocument.DocumentElement); 521 | } 522 | } 523 | } 524 | } 525 | 526 | private void FillOriginalDocumentExpandNode(XmlNode node) 527 | { 528 | FillAttributes(node); 529 | foreach (XmlNode childNode in node.ChildNodes) 530 | { 531 | if (childNode.ChildNodes.Count > 0 && !XmlUtils.IsTextNode(childNode)) 532 | { 533 | FillOriginalDocumentExpandNode(childNode); 534 | } 535 | else 536 | { 537 | if (_listViewItemHashtable[XmlUtils.BuildNodePath(childNode)] is ListViewItem item) 538 | { 539 | if (XmlUtils.ContainsText(childNode)) 540 | { 541 | try 542 | { 543 | childNode.InnerXml = item.SubItems[2].Text; 544 | } 545 | catch 546 | { 547 | childNode.InnerText = item.SubItems[2].Text; 548 | } 549 | } 550 | else 551 | { 552 | childNode.InnerText = item.SubItems[2].Text; 553 | } 554 | } 555 | FillAttributes(childNode); 556 | } 557 | } 558 | } 559 | 560 | private void FillAttributes(XmlNode node) 561 | { 562 | if (node.Attributes == null) 563 | return; 564 | 565 | foreach (XmlNode attribute in node.Attributes) 566 | { 567 | var item = _listViewItemHashtable[XmlUtils.BuildNodePath(attribute)] as ListViewItem; 568 | if (item != null) 569 | { 570 | attribute.InnerText = item.SubItems[2].Text; 571 | } 572 | } 573 | } 574 | 575 | private void Form1KeyDown(object sender, KeyEventArgs e) 576 | { 577 | if (listViewLanguageTags.Items.Count == 0) 578 | return; 579 | 580 | if (e.Control && e.KeyCode == Keys.Down) 581 | { 582 | if (listViewLanguageTags.SelectedItems.Count == 0) 583 | listViewLanguageTags.Items[0].Selected = true; 584 | 585 | int index = listViewLanguageTags.SelectedItems[0].Index + 1; 586 | if (index < listViewLanguageTags.Items.Count) 587 | { 588 | DeSelectListViewItems(); 589 | listViewLanguageTags.Items[index].Selected = true; 590 | listViewLanguageTags.EnsureVisible(index); 591 | } 592 | 593 | e.Handled = true; 594 | e.SuppressKeyPress = true; 595 | ActiveControl = textBoxCurrentText; 596 | } 597 | else if (e.Control && e.KeyCode == Keys.Up) 598 | { 599 | if (listViewLanguageTags.SelectedItems.Count == 0) 600 | listViewLanguageTags.Items[0].Selected = true; 601 | 602 | int index = listViewLanguageTags.SelectedItems[0].Index - 1; 603 | if (index >= 0) 604 | { 605 | DeSelectListViewItems(); 606 | listViewLanguageTags.Items[index].Selected = true; 607 | listViewLanguageTags.EnsureVisible(index); 608 | } 609 | e.Handled = true; 610 | e.SuppressKeyPress = true; 611 | ActiveControl = textBoxCurrentText; 612 | } 613 | else if (e.KeyCode == Keys.F6) 614 | { 615 | ButtonGoToNextBlankLineClick(null, null); 616 | } 617 | else if (e.KeyCode == Keys.F3 && !e.Control && !e.Alt) 618 | { 619 | if (_formFind == null || _formFind.SearchText.Length == 0) 620 | { 621 | findToolStripMenuItem_Click(this, null); 622 | } 623 | else 624 | { 625 | FindNext(); 626 | } 627 | e.Handled = true; 628 | e.SuppressKeyPress = true; 629 | } 630 | } 631 | 632 | private void GoogleTranslateSelectedLinesToolStripMenuItemClick(object sender, EventArgs e) 633 | { 634 | GoogleTranslateSelectedLines(); 635 | } 636 | 637 | /// 638 | /// Translate Text using Google Translate API's 639 | /// Google URL - https://www.google.com/translate_t?hl=en&ie=UTF8&text={0}&langpair={1} 640 | /// 641 | /// Input string 642 | /// 2 letter Language Pair, delimited by "|". 643 | /// E.g. "ar|en" language pair means to translate from Arabic to English 644 | /// Translated to String 645 | public static string TranslateTextViaScreenScraping(string input, string languagePair) 646 | { 647 | input = input.Replace(Environment.NewLine, "
").Trim(); 648 | input = input.Replace("'", "'"); 649 | 650 | //string url = String.Format("https://www.google.com/translate_t?hl=en&ie=UTF8&text={0}&langpair={1}", HttpUtility.UrlEncode(input), languagePair); 651 | var url = $"https://translate.google.com/?hl=en&eotf=1&sl={languagePair.Substring(0, 2)}&tl={languagePair.Substring(3)}&q={HttpUtility.UrlEncode(input)}"; 652 | 653 | var webClient = new WebClient { Encoding = Encoding.Default }; 654 | var result = webClient.DownloadString(url); 655 | var startIndex = result.IndexOf(" 0) 658 | { 659 | startIndex = result.IndexOf("", startIndex, StringComparison.Ordinal); 663 | if (startIndex > 0) 664 | { 665 | startIndex++; 666 | int endIndex = result.IndexOf("", startIndex, StringComparison.Ordinal); 667 | string translatedText = result.Substring(startIndex, endIndex - startIndex); 668 | string test = HttpUtility.HtmlDecode(translatedText); 669 | sb.Append(test); 670 | startIndex = result.IndexOf("", Environment.NewLine); 676 | res = res.Replace("
", Environment.NewLine); 677 | res = res.Replace("< BR />", Environment.NewLine); 678 | res = res.Replace("
", Environment.NewLine); 679 | res = res.Replace("
", Environment.NewLine); 680 | res = res.Replace("
", Environment.NewLine); 681 | return res.Trim(); 682 | } 683 | 684 | private void GoogleTranslateSelectedLines() 685 | { 686 | if (string.IsNullOrEmpty(_secondLanguageFileName)) 687 | return; 688 | 689 | if (comboBoxFrom.SelectedItem == null || comboBoxTo.SelectedItem == null) 690 | { 691 | MessageBox.Show("From/to language not selected"); 692 | return; 693 | } 694 | 695 | int skipped = 0; 696 | int translated = 0; 697 | string oldText = string.Empty; 698 | string newText = string.Empty; 699 | 700 | toolStripStatusLabel1.Text = "Translating via Google Translate. Please wait..."; 701 | Refresh(); 702 | 703 | var translator = new GoogleTranslator1(); 704 | Cursor = Cursors.WaitCursor; 705 | var sb = new StringBuilder(); 706 | var res = new StringBuilder(); 707 | var oldLines = new List(); 708 | var list = new List(); 709 | foreach (ListViewItem item in listViewLanguageTags.SelectedItems) 710 | { 711 | oldText = item.SubItems[1].Text; 712 | oldText = string.Join(Environment.NewLine, oldText.SplitToLines()); 713 | oldLines.Add(oldText); 714 | var urlEncode = HttpUtility.UrlEncode(sb + newText); 715 | if (urlEncode.Length >= 1000) 716 | { 717 | res.Append(TranslateTextViaScreenScraping(sb.ToString(), (comboBoxFrom.SelectedItem as ComboBoxItem).Value + "|" + (comboBoxTo.SelectedItem as ComboBoxItem).Value)); 718 | sb = new StringBuilder(); 719 | } 720 | list.Add(oldText); 721 | sb.Append("== " + oldText + " "); 722 | } 723 | var log = new StringBuilder(); 724 | var lines = translator.Translate(((ComboBoxItem)comboBoxFrom.SelectedItem).Value, ((ComboBoxItem)comboBoxTo.SelectedItem).Value, list, log).ToList(); 725 | if (listViewLanguageTags.SelectedItems.Count != lines.Count) 726 | { 727 | MessageBox.Show("Error getting/decoding translation from google!"); 728 | Cursor = Cursors.Default; 729 | return; 730 | } 731 | 732 | int index = 0; 733 | foreach (ListViewItem item in listViewLanguageTags.SelectedItems) 734 | { 735 | string s = lines[index]; 736 | string cleanText = s.Replace("

", string.Empty).Trim(); 737 | cleanText = cleanText.Replace(" ...", "..."); 738 | cleanText = cleanText.Replace("
", Environment.NewLine); 739 | cleanText = cleanText.Replace("
", Environment.NewLine); 740 | cleanText = cleanText.Replace("
", Environment.NewLine); 741 | cleanText = cleanText.Replace(Environment.NewLine + " ", Environment.NewLine); 742 | newText = cleanText; 743 | 744 | oldText = oldLines[index]; 745 | if (oldText.Contains("{0:")) 746 | { 747 | newText = oldText; 748 | } 749 | else 750 | { 751 | if (!oldText.Contains(" / ")) 752 | newText = newText.Replace(" / ", "/"); 753 | 754 | if (!oldText.Contains(" ...")) 755 | newText = newText.Replace(" ...", "..."); 756 | 757 | if (!oldText.Contains("& ")) 758 | newText = newText.Replace("& ", "&"); 759 | 760 | if (!oldText.Contains("# ")) 761 | newText = newText.Replace("# ", "#"); 762 | 763 | if (!oldText.Contains("@ ")) 764 | newText = newText.Replace("@ ", "@"); 765 | 766 | if (oldText.Contains("{0}")) 767 | { 768 | for (int i = 0; i < 50; i++) 769 | newText = newText.Replace("(" + i + ")", "{" + i + "}"); 770 | } 771 | translated++; 772 | } 773 | item.SubItems[2].Text = newText; 774 | _change = true; 775 | index++; 776 | } 777 | 778 | 779 | Cursor = Cursors.Default; 780 | if (translated == 1 && skipped == 0) 781 | { 782 | toolStripStatusLabel1.Text = "One line translated: '" + StringUtils.Max50(oldText) + "' => '" + StringUtils.Max50(newText) + "'"; 783 | } 784 | else 785 | { 786 | if (translated == 1) 787 | toolStripStatusLabel1.Text = "One line translated"; 788 | else 789 | toolStripStatusLabel1.Text = translated + " lines translated"; 790 | if (skipped > 0) 791 | toolStripStatusLabel1.Text += ", " + skipped + " line(s) skipped"; 792 | } 793 | ListViewLanguageTagsSelectedIndexChanged(null, null); 794 | } 795 | 796 | private void translateViaGoogleToolStripMenuItem_Click(object sender, EventArgs e) 797 | { 798 | GoogleTranslateSelectedLines(); 799 | } 800 | 801 | private void setValueFromMasterToolStripMenuItem_Click(object sender, EventArgs e) 802 | { 803 | if (string.IsNullOrEmpty(_secondLanguageFileName)) 804 | { 805 | return; 806 | } 807 | 808 | var transferred = 0; 809 | var oldText = string.Empty; 810 | var newText = string.Empty; 811 | foreach (ListViewItem item in listViewLanguageTags.SelectedItems) 812 | { 813 | oldText = item.SubItems[2].Text; 814 | newText = item.SubItems[1].Text; 815 | transferred++; 816 | item.SubItems[2].Text = newText; 817 | _change = true; 818 | } 819 | 820 | if (transferred == 1) 821 | { 822 | toolStripStatusLabel1.Text = "One line transfered from master: '" + oldText + "' => '" + newText + "'"; 823 | } 824 | else 825 | { 826 | toolStripStatusLabel1.Text = transferred + " line(s) transfered from master"; 827 | } 828 | 829 | ListViewLanguageTagsSelectedIndexChanged(null, null); 830 | } 831 | 832 | public static void FillComboWithLanguages(ComboBox comboBox) 833 | { 834 | foreach (var pair in new GoogleTranslator1().GetTranslationPairs()) 835 | { 836 | comboBox.Items.Add(new ComboBoxItem(pair.Name, pair.Code)); 837 | } 838 | } 839 | 840 | private void ToolStripMenuItem1Click(object sender, EventArgs e) 841 | { 842 | if (_change && listViewLanguageTags.Columns.Count == 3 && 843 | MessageBox.Show("Changes will be lost. Continue?", "Continue", MessageBoxButtons.YesNo) == DialogResult.No) 844 | { 845 | return; 846 | } 847 | 848 | MakeNew(); 849 | toolStripStatusLabel1.Text = "New"; 850 | } 851 | 852 | private void SaveAsToolStripMenuItemClick(object sender, EventArgs e) 853 | { 854 | if (_originalDocument == null) 855 | { 856 | return; 857 | } 858 | 859 | saveFileDialog1.Title = "Save language file as..."; 860 | saveFileDialog1.DefaultExt = ".xml"; 861 | saveFileDialog1.Filter = "Xml files|*.xml" + "|All files|*.*"; 862 | saveFileDialog1.Title = "Open language master file"; 863 | if (saveFileDialog1.ShowDialog() == DialogResult.OK) 864 | { 865 | _secondLanguageFileName = saveFileDialog1.FileName; 866 | FillOriginalDocumentFromSecondLanguage(); 867 | _originalDocument.Save(saveFileDialog1.FileName); 868 | _change = false; 869 | toolStripStatusLabel1.Text = "File saved as " + _secondLanguageFileName; 870 | } 871 | } 872 | 873 | private void TextBoxCurrentTextKeyDown(object sender, KeyEventArgs e) 874 | { 875 | if (!e.Control && !e.Alt) 876 | { 877 | _change = true; 878 | } 879 | } 880 | 881 | private void SaveToolStripMenuItemClick(object sender, EventArgs e) 882 | { 883 | if (string.IsNullOrEmpty(_secondLanguageFileName)) 884 | { 885 | SaveAsToolStripMenuItemClick(null, null); 886 | } 887 | else 888 | { 889 | FillOriginalDocumentFromSecondLanguage(); 890 | 891 | using (var sw = new StringWriter()) 892 | { 893 | using (var xw = XmlWriter.Create(sw, new XmlWriterSettings { Indent = true, Encoding = Encoding.UTF8 })) 894 | { 895 | _originalDocument.Save(xw); 896 | var s = sw.ToString(); 897 | if (s.Contains("Subtitle Edit")) 898 | { 899 | s = s.Replace("", ""); 900 | } 901 | s = s.Replace("encoding=\"utf-16\"?", "encoding=\"utf-8\"?"); 902 | File.WriteAllText(_secondLanguageFileName, s, Encoding.UTF8); 903 | } 904 | } 905 | _change = false; 906 | toolStripStatusLabel1.Text = "File saved - " + _secondLanguageFileName; 907 | } 908 | } 909 | 910 | private void Form1FormClosing(object sender, FormClosingEventArgs e) 911 | { 912 | if (_change && listViewLanguageTags.Columns.Count == 3 && 913 | MessageBox.Show("Changes will be lost. Continue?", "Continue", MessageBoxButtons.YesNo) == DialogResult.No) 914 | { 915 | e.Cancel = true; 916 | } 917 | } 918 | 919 | private void ListViewLanguageTagsDragEnter(object sender, DragEventArgs e) 920 | { // make sure they're actually dropping files (not text or anything else) 921 | if (e.Data.GetDataPresent(DataFormats.FileDrop, false)) 922 | { 923 | e.Effect = DragDropEffects.All; 924 | } 925 | } 926 | 927 | private void ListViewLanguageTagsDragDrop(object sender, DragEventArgs e) 928 | { 929 | var files = (string[])e.Data.GetData(DataFormats.FileDrop); 930 | 931 | if (!string.IsNullOrEmpty(_secondLanguageFileName)) 932 | { 933 | MessageBox.Show("Two files already loaded"); 934 | return; 935 | } 936 | 937 | if (files.Length == 1) 938 | { 939 | 940 | var fileName = files[0]; 941 | var fi = new FileInfo(fileName); 942 | if (fi.Length < 1024 * 1024 * 20) // max 20 mb 943 | { 944 | if (treeView1.Nodes.Count == 0) 945 | { 946 | OpenFirstFile(fileName); 947 | } 948 | else 949 | { 950 | OpenSecondFile(fileName); 951 | } 952 | } 953 | else 954 | { 955 | MessageBox.Show(fileName + " is too large (max 20 mb)"); 956 | } 957 | } 958 | else 959 | { 960 | MessageBox.Show("Only file drop supported"); 961 | } 962 | 963 | } 964 | 965 | private void Form1Load(object sender, EventArgs e) 966 | { 967 | var args = Environment.GetCommandLineArgs(); 968 | if (args.Length > 1) 969 | { 970 | if (File.Exists(args[1])) 971 | { 972 | OpenFirstFile(args[1]); 973 | } 974 | 975 | if (args.Length > 2 && File.Exists(args[2])) 976 | { 977 | OpenSecondFile(args[2]); 978 | } 979 | } 980 | 981 | } 982 | 983 | private void ButtonGoToNextBlankLineClick(object sender, EventArgs e) 984 | { 985 | var index = 0; 986 | if (listViewLanguageTags.SelectedItems.Count > 0) 987 | { 988 | index = listViewLanguageTags.SelectedItems[0].Index + 1; 989 | } 990 | 991 | for (; index < listViewLanguageTags.Items.Count; index++) 992 | { 993 | if (listViewLanguageTags.Items[index].SubItems.Count > 1 && string.IsNullOrEmpty(listViewLanguageTags.Items[index].SubItems[2].Text)) 994 | { 995 | foreach (ListViewItem item in listViewLanguageTags.SelectedItems) 996 | { 997 | item.Selected = false; 998 | } 999 | 1000 | listViewLanguageTags.Items[index].Selected = true; 1001 | listViewLanguageTags.Items[index].EnsureVisible(); 1002 | return; 1003 | } 1004 | } 1005 | } 1006 | 1007 | private void HighLightLinesWithSameText() 1008 | { 1009 | foreach (ListViewItem item in listViewLanguageTags.Items) 1010 | { 1011 | if (item.SubItems.Count == 3) 1012 | { 1013 | if (item.SubItems[1].Text.Trim() == item.SubItems[2].Text.Trim()) 1014 | { 1015 | item.BackColor = Color.LightYellow; 1016 | item.UseItemStyleForSubItems = true; 1017 | } 1018 | else if (item.SubItems[2].Text.Trim().Length == 0) 1019 | { 1020 | item.BackColor = Color.LightPink; 1021 | item.UseItemStyleForSubItems = true; 1022 | } 1023 | else 1024 | { 1025 | item.BackColor = listViewLanguageTags.BackColor; 1026 | item.UseItemStyleForSubItems = true; 1027 | } 1028 | } 1029 | } 1030 | } 1031 | 1032 | private void toolStripMenuItem2_Click(object sender, EventArgs e) 1033 | { 1034 | openFileDialog1.FileName = string.Empty; 1035 | openFileDialog1.DefaultExt = ".xml"; 1036 | openFileDialog1.Filter = "Xml files|*.xml" + "|All files|*.*"; 1037 | openFileDialog1.Title = "Open English translation base file"; 1038 | 1039 | var doc = new XmlDocument(); 1040 | try 1041 | { 1042 | const string url = "https://raw.githubusercontent.com/SubtitleEdit/subtitleedit/main/LanguageBaseEnglish.xml"; 1043 | var wc = new WebClient(); 1044 | var xml = wc.DownloadString(url); 1045 | MakeNew(); 1046 | doc.LoadXml(xml); 1047 | OpenFirstXmlDocument(doc); 1048 | } 1049 | catch (Exception ex) 1050 | { 1051 | MessageBox.Show("Error: " + ex.Message); 1052 | return; 1053 | } 1054 | 1055 | OpenSecondFile(); 1056 | } 1057 | 1058 | private void findToolStripMenuItem_Click(object sender, EventArgs e) 1059 | { 1060 | if (_formFind == null) 1061 | { 1062 | _formFind = new Find(); 1063 | } 1064 | 1065 | if (_formFind.ShowDialog(this) == DialogResult.OK) 1066 | { 1067 | FindNext(); 1068 | } 1069 | } 1070 | 1071 | private void FindNext() 1072 | { 1073 | if (_formFind.SearchText.Length == 0) 1074 | return; 1075 | 1076 | var index = 0; 1077 | if (listViewLanguageTags.SelectedItems.Count > 0) 1078 | { 1079 | index = listViewLanguageTags.SelectedItems[0].Index + 1; 1080 | } 1081 | while (index < listViewLanguageTags.Items.Count) 1082 | { 1083 | if (_formFind.SearchTags) 1084 | { 1085 | if (listViewLanguageTags.Items[index].Text.ToLower().Contains(_formFind.SearchText.ToLower())) 1086 | { 1087 | SelectOnlyThis(index); 1088 | return; 1089 | } 1090 | } 1091 | else 1092 | { 1093 | if (listViewLanguageTags.Items[index].SubItems[0].Text.ToLower().Contains(_formFind.SearchText) || 1094 | listViewLanguageTags.Items[index].SubItems[1].Text.ToLower().Contains(_formFind.SearchText)) 1095 | { 1096 | SelectOnlyThis(index); 1097 | return; 1098 | } 1099 | } 1100 | index++; 1101 | } 1102 | } 1103 | 1104 | private void SelectOnlyThis(int index) 1105 | { 1106 | foreach (ListViewItem selectedItem in listViewLanguageTags.SelectedItems) 1107 | { 1108 | selectedItem.Selected = false; 1109 | } 1110 | listViewLanguageTags.Items[index].Selected = true; 1111 | listViewLanguageTags.Items[index].EnsureVisible(); 1112 | listViewLanguageTags.Items[index].Focused = true; 1113 | } 1114 | 1115 | private void listViewLanguageTags_DoubleClick(object sender, EventArgs e) 1116 | { 1117 | if (listViewLanguageTags.SelectedItems.Count != 1) 1118 | { 1119 | return; 1120 | } 1121 | 1122 | var node = listViewLanguageTags.SelectedItems[0].Tag as XmlNode; 1123 | if (node == null) 1124 | { 1125 | return; 1126 | } 1127 | 1128 | foreach (TreeNode treeNode in treeView1.Nodes) 1129 | { 1130 | if (treeNode.Tag == node) 1131 | { 1132 | treeView1.SelectedNode = treeNode; 1133 | return; 1134 | } 1135 | foreach (TreeNode subTreeNode in treeNode.Nodes) 1136 | { 1137 | if (subTreeNode.Tag == node) 1138 | { 1139 | treeView1.SelectedNode = subTreeNode; 1140 | return; 1141 | } 1142 | } 1143 | } 1144 | } 1145 | 1146 | private void Main_Resize(object sender, EventArgs e) 1147 | { 1148 | Main_ResizeEnd(null, null); 1149 | } 1150 | 1151 | private void Main_ResizeEnd(object sender, EventArgs e) 1152 | { 1153 | if (listViewLanguageTags.Columns.Count > 0) 1154 | { 1155 | var w = 0; 1156 | for (var i = 0; i < listViewLanguageTags.Columns.Count - 1; i++) 1157 | { 1158 | w += listViewLanguageTags.Columns[i].Width; 1159 | } 1160 | 1161 | listViewLanguageTags.Columns[listViewLanguageTags.Columns.Count - 1].Width = listViewLanguageTags.Width - 25 - w; 1162 | } 1163 | } 1164 | } 1165 | } -------------------------------------------------------------------------------- /src/XmlContentTranslator/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows.Forms; 4 | 5 | namespace XmlContentTranslator 6 | { 7 | static class Program 8 | { 9 | /// 10 | /// The main entry point for the application. 11 | /// 12 | [STAThread] 13 | static void Main() 14 | { 15 | Application.EnableVisualStyles(); 16 | Application.SetCompatibleTextRenderingDefault(false); 17 | Application.Run(new Main()); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Xml Content Translator")] 9 | [assembly: AssemblyDescription("Translate content of xml files")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Nikse.dk")] 12 | [assembly: AssemblyProduct("Xml Content Translator")] 13 | [assembly: AssemblyCopyright("Copyright © 2022")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("cbc25f85-5713-4ba7-a42f-c653265a192f")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.12.0.0")] 36 | [assembly: AssemblyFileVersion("1.12.0.0")] 37 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace XmlContentTranslator.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("XmlContentTranslator.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace XmlContentTranslator.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.3.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/StringUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace XmlContentTranslator 7 | { 8 | public static class StringUtils 9 | { 10 | public static string Max50(string text) 11 | { 12 | if (text.Length > 50) 13 | return text.Substring(0, 50).Trim() + "..."; 14 | return text; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Translator/Configuration.cs: -------------------------------------------------------------------------------- 1 | namespace XmlContentTranslator.Translator 2 | { 3 | internal static class Configuration 4 | { 5 | public static double CurrentFrameRate = 23.976; 6 | public static string ListViewLineSeparatorString = "
"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Translator/Formatting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace XmlContentTranslator.Translator 6 | { 7 | public class Formatting 8 | { 9 | private bool Italic { get; set; } 10 | private bool ItalicTwoLines { get; set; } 11 | private string StartTags { get; set; } 12 | private bool AutoBreak { get; set; } 13 | private bool SquareBrackets { get; set; } 14 | private bool SquareBracketsUppercase { get; set; } 15 | 16 | public string SetTagsAndReturnTrimmed(string text, string source) 17 | { 18 | text = text.Trim(); 19 | 20 | // SSA/ASS tags 21 | if (text.StartsWith("{\\")) 22 | { 23 | var endIndex = text.IndexOf('}'); 24 | if (endIndex > 0) 25 | { 26 | StartTags = text.Substring(0, endIndex + 1); 27 | text = text.Remove(0, endIndex + 1).Trim(); 28 | } 29 | } 30 | 31 | // Italic tags 32 | if (text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal) && text.Contains("" + Environment.NewLine + "") && Utilities.GetNumberOfLines(text) == 2 && Utilities.CountTagInText(text, "") == 1) 33 | { 34 | ItalicTwoLines = true; 35 | text = HtmlUtil.RemoveOpenCloseTags(text, HtmlUtil.TagItalic); 36 | } 37 | else if (text.StartsWith("", StringComparison.Ordinal) && text.EndsWith("", StringComparison.Ordinal) && Utilities.CountTagInText(text, "") == 1) 38 | { 39 | Italic = true; 40 | text = text.Substring(3, text.Length - 7); 41 | } 42 | 43 | // Un-break line 44 | var allowedLanguages = new List { "en", "da", "nl", "de", "sv", "nb", "fr", "it" }; 45 | if (allowedLanguages.Contains(source)) 46 | { 47 | var lines = HtmlUtil.RemoveHtmlTags(text).SplitToLines(); 48 | if (lines.Length == 2 && !string.IsNullOrEmpty(lines[0]) && !string.IsNullOrEmpty(lines[1]) && 49 | char.IsLetterOrDigit(lines[0][lines[0].Length - 1]) && 50 | char.IsLower(lines[1][0])) 51 | { 52 | text = string.Join(" ", text.SplitToLines()).Replace(" ", " "); 53 | AutoBreak = true; 54 | } 55 | } 56 | 57 | // Square brackets 58 | if (text.StartsWith("[", StringComparison.Ordinal) && text.EndsWith("]", StringComparison.Ordinal) && 59 | Utilities.GetNumberOfLines(text) == 1 && Utilities.CountTagInText(text, "[") == 1 && 60 | Utilities.GetNumberOfLines(text) == 1 && Utilities.CountTagInText(text, "]") == 1) 61 | { 62 | if (text == text.ToUpperInvariant()) 63 | SquareBracketsUppercase = true; 64 | else 65 | SquareBrackets = true; 66 | text = text.Replace("[", string.Empty).Replace("]", string.Empty); 67 | } 68 | 69 | return text.Trim(); 70 | } 71 | 72 | public string ReAddFormatting(string text) 73 | { 74 | // Auto-break line 75 | if (AutoBreak) 76 | { 77 | text = Utilities.AutoBreakLine(text); 78 | } 79 | 80 | // Square brackets 81 | if (SquareBracketsUppercase) 82 | { 83 | text = "[" + text.ToUpperInvariant().Trim() + "]"; 84 | } 85 | else if (SquareBrackets) 86 | { 87 | text = "[" + text.Trim() + "]"; 88 | } 89 | 90 | // Italic tags 91 | if (ItalicTwoLines) 92 | { 93 | var sb = new StringBuilder(); 94 | foreach (var line in text.SplitToLines()) 95 | { 96 | sb.AppendLine("" + line + ""); 97 | } 98 | text = sb.ToString().Trim(); 99 | } 100 | else if (Italic) 101 | { 102 | text = "" + text + ""; 103 | } 104 | 105 | // SSA/ASS tags 106 | text = StartTags + text; 107 | 108 | return text; 109 | } 110 | 111 | 112 | private int NumberOfLines { get; set; } 113 | 114 | public string Unbreak(string text, string source) 115 | { 116 | NumberOfLines = source.SplitToLines().Length; 117 | return text.Replace(Environment.NewLine, " ").Replace(" ", " "); 118 | } 119 | 120 | public string Rebreak(string text) 121 | { 122 | if (NumberOfLines == 1) 123 | return text; 124 | return Utilities.AutoBreakLine(text); 125 | } 126 | 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Translator/GoogleTranslator1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | 8 | namespace XmlContentTranslator.Translator 9 | { 10 | /// 11 | /// Google translate via Google V1 API - see https://cloud.google.com/translate/ 12 | /// 13 | public class GoogleTranslator1 : ITranslator 14 | { 15 | private const char SplitChar = '\n'; 16 | 17 | public List GetTranslationPairs() 18 | { 19 | return new List 20 | { 21 | new TranslationPair("AFRIKAANS", "af"), 22 | new TranslationPair("ALBANIAN", "sq"), 23 | new TranslationPair("AMHARIC", "am"), 24 | new TranslationPair("ARABIC", "ar"), 25 | new TranslationPair("ARMENIAN", "hy"), 26 | new TranslationPair("AZERBAIJANI", "az"), 27 | new TranslationPair("BASQUE", "eu"), 28 | new TranslationPair("BELARUSIAN", "be"), 29 | new TranslationPair("BENGALI", "bn"), 30 | new TranslationPair("BOSNIAN", "bs"), 31 | new TranslationPair("BULGARIAN", "bg"), 32 | new TranslationPair("BURMESE", "my"), 33 | new TranslationPair("CATALAN", "ca"), 34 | new TranslationPair("CEBUANO", "ceb"), 35 | new TranslationPair("CHICHEWA", "ny"), 36 | new TranslationPair("CHINESE", "zh"), 37 | new TranslationPair("CHINESE_SIMPLIFIED", "zh-CN"), 38 | new TranslationPair("CHINESE_TRADITIONAL", "zh-TW"), 39 | new TranslationPair("CORSICAN", "co"), 40 | new TranslationPair("CROATIAN", "hr"), 41 | new TranslationPair("CZECH", "cs"), 42 | new TranslationPair("DANISH", "da"), 43 | new TranslationPair("DUTCH", "nl"), 44 | new TranslationPair("ENGLISH", "en"), 45 | new TranslationPair("ESPERANTO", "eo"), 46 | new TranslationPair("ESTONIAN", "et"), 47 | new TranslationPair("FILIPINO", "tl"), 48 | new TranslationPair("FINNISH", "fi"), 49 | new TranslationPair("FRENCH", "fr"), 50 | new TranslationPair("FRISIAN", "fy"), 51 | new TranslationPair("GALICIAN", "gl"), 52 | new TranslationPair("GEORGIAN", "ka"), 53 | new TranslationPair("GERMAN", "de"), 54 | new TranslationPair("GREEK", "el"), 55 | new TranslationPair("GUJARATI", "gu"), 56 | new TranslationPair("HAITIAN CREOLE", "ht"), 57 | new TranslationPair("HAUSA", "ha"), 58 | new TranslationPair("HAWAIIAN", "haw"), 59 | new TranslationPair("HEBREW", "iw"), 60 | new TranslationPair("HINDI", "hi"), 61 | new TranslationPair("HMOUNG", "hmn"), 62 | new TranslationPair("HUNGARIAN", "hu"), 63 | new TranslationPair("ICELANDIC", "is"), 64 | new TranslationPair("IGBO", "ig"), 65 | new TranslationPair("INDONESIAN", "id"), 66 | new TranslationPair("IRISH", "ga"), 67 | new TranslationPair("ITALIAN", "it"), 68 | new TranslationPair("JAPANESE", "ja"), 69 | new TranslationPair("JAVANESE", "jw"), 70 | new TranslationPair("KANNADA", "kn"), 71 | new TranslationPair("KAZAKH", "kk"), 72 | new TranslationPair("KHMER", "km"), 73 | new TranslationPair("KOREAN", "ko"), 74 | new TranslationPair("KURDISH", "ku"), 75 | new TranslationPair("KYRGYZ", "ky"), 76 | new TranslationPair("LAO", "lo"), 77 | new TranslationPair("LATIN", "la"), 78 | new TranslationPair("LATVIAN", "lv"), 79 | new TranslationPair("LITHUANIAN", "lt"), 80 | new TranslationPair("LUXEMBOURGISH", "lb"), 81 | new TranslationPair("MACEDONIAN", "mk"), 82 | new TranslationPair("MALAY", "ms"), 83 | new TranslationPair("MALAGASY", "mg"), 84 | new TranslationPair("MALAYALAM", "ml"), 85 | new TranslationPair("MALTESE", "mt"), 86 | new TranslationPair("MAORI", "mi"), 87 | new TranslationPair("MARATHI", "mr"), 88 | new TranslationPair("MONGOLIAN", "mn"), 89 | new TranslationPair("MYANMAR", "my"), 90 | new TranslationPair("NEPALI", "ne"), 91 | new TranslationPair("NORWEGIAN", "no"), 92 | new TranslationPair("PASHTO", "ps"), 93 | new TranslationPair("PERSIAN", "fa"), 94 | new TranslationPair("POLISH", "pl"), 95 | new TranslationPair("PORTUGUESE", "pt"), 96 | new TranslationPair("PUNJABI", "pa"), 97 | new TranslationPair("ROMANIAN", "ro"), 98 | new TranslationPair("ROMANJI", "romanji"), 99 | new TranslationPair("RUSSIAN", "ru"), 100 | new TranslationPair("SAMOAN", "sm"), 101 | new TranslationPair("SCOTS GAELIC", "gd"), 102 | new TranslationPair("SERBIAN", "sr"), 103 | new TranslationPair("SESOTHO", "st"), 104 | new TranslationPair("SHONA", "sn"), 105 | new TranslationPair("SINDHI", "sd"), 106 | new TranslationPair("SINHALA", "si"), 107 | new TranslationPair("SLOVAK", "sk"), 108 | new TranslationPair("SLOVENIAN", "sl"), 109 | new TranslationPair("SOMALI", "so"), 110 | new TranslationPair("SPANISH", "es"), 111 | new TranslationPair("SUNDANESE", "su"), 112 | new TranslationPair("SWAHILI", "sw"), 113 | new TranslationPair("SWEDISH", "sv"), 114 | new TranslationPair("TAJIK", "tg"), 115 | new TranslationPair("TAMIL", "ta"), 116 | new TranslationPair("TELUGU", "te"), 117 | new TranslationPair("THAI", "th"), 118 | new TranslationPair("TURKISH", "tr"), 119 | new TranslationPair("UKRAINIAN", "uk"), 120 | new TranslationPair("URDU", "ur"), 121 | new TranslationPair("UZBEK", "uz"), 122 | new TranslationPair("VIETNAMESE", "vi"), 123 | new TranslationPair("WELSH", "cy"), 124 | new TranslationPair("XHOSA", "xh"), 125 | new TranslationPair("YIDDISH", "yi"), 126 | new TranslationPair("YORUBA", "yo"), 127 | new TranslationPair("ZULU", "zu"), 128 | }; 129 | } 130 | 131 | public string GetName() 132 | { 133 | return "Google translate (old)"; 134 | } 135 | 136 | public string GetUrl() 137 | { 138 | return "https://translate.google.com/"; 139 | } 140 | 141 | public List Translate(string sourceLanguage, string targetLanguage, List paragraphs, StringBuilder log) 142 | { 143 | string result; 144 | var input = new StringBuilder(); 145 | var formattings = new Formatting[paragraphs.Count]; 146 | for (var index = 0; index < paragraphs.Count; index++) 147 | { 148 | var p = paragraphs[index]; 149 | var f = new Formatting(); 150 | formattings[index] = f; 151 | if (input.Length > 0) 152 | { 153 | input.Append(" " + SplitChar + " "); 154 | } 155 | var text = f.SetTagsAndReturnTrimmed(TranslationHelper.PreTranslate(p.Replace(SplitChar.ToString(), string.Empty), sourceLanguage), sourceLanguage); 156 | text = f.Unbreak(text, p); 157 | input.Append(text); 158 | } 159 | 160 | using (var wc = new WebClient()) 161 | { 162 | string url = $"https://translate.googleapis.com/translate_a/single?client=gtx&sl={sourceLanguage}&tl={targetLanguage}&dt=t&q={Uri.EscapeDataString(input.ToString())}"; 163 | wc.Encoding = Encoding.UTF8; 164 | wc.Headers.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"); 165 | result = wc.DownloadString(url).Trim(); 166 | } 167 | 168 | var sbAll = new StringBuilder(); 169 | int count = 0; 170 | int i = 1; 171 | int level = result.StartsWith('[') ? 1 : 0; 172 | while (i < result.Length - 1) 173 | { 174 | var sb = new StringBuilder(); 175 | var start = false; 176 | for (; i < result.Length - 1; i++) 177 | { 178 | var c = result[i]; 179 | if (start) 180 | { 181 | if (c == '"' && result[i - 1] != '\\') 182 | { 183 | count++; 184 | if (count % 2 == 1 && level > 2 && level < 5) // even numbers are original text, level 3 is translation 185 | sbAll.Append(" " + sb); 186 | i++; 187 | break; 188 | } 189 | sb.Append(c); 190 | } 191 | else if (c == '"') 192 | { 193 | start = true; 194 | } 195 | else if (c == '[') 196 | { 197 | level++; 198 | } 199 | else if (c == ']') 200 | { 201 | level--; 202 | } 203 | } 204 | } 205 | 206 | var res = sbAll.ToString().Trim(); 207 | res = Regex.Unescape(res); 208 | var lines = res.SplitToLines().ToList(); 209 | var resultList = new List(); 210 | for (var index = 0; index < lines.Count; index++) 211 | { 212 | var line = lines[index]; 213 | var s = Json.DecodeJsonText(line); 214 | s = string.Join(Environment.NewLine, s.SplitToLines()); 215 | s = TranslationHelper.PostTranslate(s, targetLanguage); 216 | s = s.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine); 217 | s = s.Replace(Environment.NewLine + " ", Environment.NewLine); 218 | s = s.Replace(Environment.NewLine + " ", Environment.NewLine); 219 | s = s.Replace(" " + Environment.NewLine, Environment.NewLine); 220 | s = s.Replace(" " + Environment.NewLine, Environment.NewLine).Trim(); 221 | if (formattings.Length > index) 222 | { 223 | s = formattings[index].ReAddFormatting(s); 224 | s = formattings[index].Rebreak(s); 225 | } 226 | 227 | resultList.Add(s); 228 | } 229 | 230 | if (resultList.Count > paragraphs.Count) 231 | { 232 | var timmedList = resultList.Where(p => !string.IsNullOrEmpty(p)).ToList(); 233 | if (timmedList.Count == paragraphs.Count) 234 | return timmedList; 235 | } 236 | 237 | if (resultList.Count < paragraphs.Count) 238 | { 239 | var splitList = SplitMergedLines(resultList, paragraphs); 240 | if (splitList.Count == paragraphs.Count) 241 | return splitList; 242 | } 243 | 244 | return resultList; 245 | } 246 | 247 | private static List SplitMergedLines(List input, List paragraphs) 248 | { 249 | var hits = 0; 250 | var results = new List(); 251 | for (var index = 0; index < input.Count; index++) 252 | { 253 | var line = input[index]; 254 | var text = paragraphs[index]; 255 | var badPoints = 0; 256 | if (text.StartsWith("[") && !line.StartsWith("[")) 257 | badPoints++; 258 | if (text.StartsWith("-") && !line.StartsWith("-")) 259 | badPoints++; 260 | if (text.Length > 0 && char.IsUpper(text[0]) && line.Length > 0 && !char.IsUpper(line[0])) 261 | badPoints++; 262 | if (text.EndsWith(".") && !line.EndsWith(".")) 263 | badPoints++; 264 | if (text.EndsWith("!") && !line.EndsWith("!")) 265 | badPoints++; 266 | if (text.EndsWith("?") && !line.EndsWith("?")) 267 | badPoints++; 268 | if (text.EndsWith(",") && !line.EndsWith(",")) 269 | badPoints++; 270 | if (text.EndsWith(":") && !line.EndsWith(":")) 271 | badPoints++; 272 | var added = false; 273 | if (badPoints > 0 && hits + input.Count < paragraphs.Count) 274 | { 275 | var percent = line.Length * 100.0 / text.Length; 276 | if (percent > 150) 277 | { 278 | var temp = Utilities.AutoBreakLine(line).SplitToLines(); 279 | if (temp.Length == 2) 280 | { 281 | hits++; 282 | results.Add(temp[0]); 283 | results.Add(temp[1]); 284 | added = true; 285 | } 286 | } 287 | } 288 | if (!added) 289 | { 290 | results.Add(line); 291 | } 292 | } 293 | 294 | if (results.Count == paragraphs.Count) 295 | return results; 296 | 297 | return input; 298 | } 299 | 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Translator/HtmlUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | using System.Text.RegularExpressions; 5 | 6 | namespace XmlContentTranslator.Translator 7 | { 8 | /// 9 | /// HTML specific string manipulations. 10 | /// 11 | public static class HtmlUtil 12 | { 13 | public const string TagItalic = "i"; 14 | public const string TagBold = "b"; 15 | public const string TagUnderline = "u"; 16 | public const string TagParagraph = "p"; 17 | public const string TagFont = "font"; 18 | public const string TagCyrillicI = "\u0456"; // Cyrillic Small Letter Byelorussian-Ukrainian i (http://graphemica.com/%D1%96) 19 | 20 | private static readonly Regex TagOpenRegex = new Regex(@"<\s*(?:/\s*)?(\w+)[^>]*>", RegexOptions.Compiled); 21 | 22 | /// 23 | /// Remove all of the specified opening and closing tags from the source HTML string. 24 | /// 25 | /// The source string to search for specified HTML tags. 26 | /// The HTML tags to remove. 27 | /// A new string without the specified opening and closing tags. 28 | public static string RemoveOpenCloseTags(string source, params string[] tags) 29 | { 30 | // This pattern matches these tag formats: 31 | // 32 | // < tag*> 33 | // 34 | // < /tag*> 35 | // 36 | // < / tag*> 37 | return TagOpenRegex.Replace( 38 | source, 39 | m => tags.Contains(m.Groups[1].Value, StringComparer.OrdinalIgnoreCase) ? string.Empty : m.Value); 40 | } 41 | 42 | /// 43 | /// Converts a string to an HTML-encoded string using named character references. 44 | /// 45 | /// The string to encode. 46 | /// An encoded string. 47 | public static string EncodeNamed(string source) 48 | { 49 | if (string.IsNullOrEmpty(source)) 50 | return string.Empty; 51 | 52 | var encoded = new StringBuilder(source.Length); 53 | foreach (var ch in source) 54 | { 55 | switch (ch) 56 | { 57 | case '<': 58 | encoded.Append("<"); 59 | break; 60 | case '>': 61 | encoded.Append(">"); 62 | break; 63 | case '"': 64 | encoded.Append("""); 65 | break; 66 | case '&': 67 | encoded.Append("&"); 68 | break; 69 | case '\'': 70 | encoded.Append("'"); 71 | break; 72 | case ' ': 73 | encoded.Append(" "); 74 | break; 75 | case '–': 76 | encoded.Append("–"); 77 | break; 78 | case '—': 79 | encoded.Append("—"); 80 | break; 81 | case '¡': 82 | encoded.Append("¡"); 83 | break; 84 | case '¿': 85 | encoded.Append("¿"); 86 | break; 87 | case '“': 88 | encoded.Append("“"); 89 | break; 90 | case '”': 91 | encoded.Append("”"); 92 | break; 93 | case '‘': 94 | encoded.Append("‘"); 95 | break; 96 | case '’': 97 | encoded.Append("’"); 98 | break; 99 | case '«': 100 | encoded.Append("«"); 101 | break; 102 | case '»': 103 | encoded.Append("»"); 104 | break; 105 | case '¢': 106 | encoded.Append("¢"); 107 | break; 108 | case '©': 109 | encoded.Append("©"); 110 | break; 111 | case '÷': 112 | encoded.Append("÷"); 113 | break; 114 | case 'µ': 115 | encoded.Append("µ"); 116 | break; 117 | case '·': 118 | encoded.Append("·"); 119 | break; 120 | case '¶': 121 | encoded.Append("¶"); 122 | break; 123 | case '±': 124 | encoded.Append("±"); 125 | break; 126 | case '€': 127 | encoded.Append("€"); 128 | break; 129 | case '£': 130 | encoded.Append("£"); 131 | break; 132 | case '®': 133 | encoded.Append("®"); 134 | break; 135 | case '§': 136 | encoded.Append("§"); 137 | break; 138 | case '™': 139 | encoded.Append("™"); 140 | break; 141 | case '¥': 142 | encoded.Append("¥"); 143 | break; 144 | case 'á': 145 | encoded.Append("á"); 146 | break; 147 | case 'Á': 148 | encoded.Append("Á"); 149 | break; 150 | case 'à': 151 | encoded.Append("à"); 152 | break; 153 | case 'À': 154 | encoded.Append("À"); 155 | break; 156 | case 'â': 157 | encoded.Append("â"); 158 | break; 159 | case 'Â': 160 | encoded.Append("Â"); 161 | break; 162 | case 'å': 163 | encoded.Append("å"); 164 | break; 165 | case 'Å': 166 | encoded.Append("Å"); 167 | break; 168 | case 'ã': 169 | encoded.Append("ã"); 170 | break; 171 | case 'Ã': 172 | encoded.Append("Ã"); 173 | break; 174 | case 'ä': 175 | encoded.Append("ä"); 176 | break; 177 | case 'Ä': 178 | encoded.Append("Ä"); 179 | break; 180 | case 'æ': 181 | encoded.Append("æ"); 182 | break; 183 | case 'Æ': 184 | encoded.Append("Æ"); 185 | break; 186 | case 'ç': 187 | encoded.Append("ç"); 188 | break; 189 | case 'Ç': 190 | encoded.Append("Ç"); 191 | break; 192 | case 'é': 193 | encoded.Append("é"); 194 | break; 195 | case 'É': 196 | encoded.Append("É"); 197 | break; 198 | case 'è': 199 | encoded.Append("è"); 200 | break; 201 | case 'È': 202 | encoded.Append("È"); 203 | break; 204 | case 'ê': 205 | encoded.Append("ê"); 206 | break; 207 | case 'Ê': 208 | encoded.Append("Ê"); 209 | break; 210 | case 'ë': 211 | encoded.Append("ë"); 212 | break; 213 | case 'Ë': 214 | encoded.Append("Ë"); 215 | break; 216 | case 'í': 217 | encoded.Append("í"); 218 | break; 219 | case 'Í': 220 | encoded.Append("Í"); 221 | break; 222 | case 'ì': 223 | encoded.Append("ì"); 224 | break; 225 | case 'Ì': 226 | encoded.Append("Ì"); 227 | break; 228 | case 'î': 229 | encoded.Append("î"); 230 | break; 231 | case 'Î': 232 | encoded.Append("Î"); 233 | break; 234 | case 'ï': 235 | encoded.Append("ï"); 236 | break; 237 | case 'Ï': 238 | encoded.Append("Ï"); 239 | break; 240 | case 'ñ': 241 | encoded.Append("ñ"); 242 | break; 243 | case 'Ñ': 244 | encoded.Append("Ñ"); 245 | break; 246 | case 'ó': 247 | encoded.Append("ó"); 248 | break; 249 | case 'Ó': 250 | encoded.Append("Ó"); 251 | break; 252 | case 'ò': 253 | encoded.Append("ò"); 254 | break; 255 | case 'Ò': 256 | encoded.Append("Ò"); 257 | break; 258 | case 'ô': 259 | encoded.Append("ô"); 260 | break; 261 | case 'Ô': 262 | encoded.Append("Ô"); 263 | break; 264 | case 'ø': 265 | encoded.Append("ø"); 266 | break; 267 | case 'Ø': 268 | encoded.Append("Ø"); 269 | break; 270 | case 'õ': 271 | encoded.Append("õ"); 272 | break; 273 | case 'Õ': 274 | encoded.Append("Õ"); 275 | break; 276 | case 'ö': 277 | encoded.Append("ö"); 278 | break; 279 | case 'Ö': 280 | encoded.Append("Ö"); 281 | break; 282 | case 'ß': 283 | encoded.Append("ß"); 284 | break; 285 | case 'ú': 286 | encoded.Append("ú"); 287 | break; 288 | case 'Ú': 289 | encoded.Append("Ú"); 290 | break; 291 | case 'ù': 292 | encoded.Append("ù"); 293 | break; 294 | case 'Ù': 295 | encoded.Append("Ù"); 296 | break; 297 | case 'û': 298 | encoded.Append("û"); 299 | break; 300 | case 'Û': 301 | encoded.Append("Û"); 302 | break; 303 | case 'ü': 304 | encoded.Append("ü"); 305 | break; 306 | case 'Ü': 307 | encoded.Append("Ü"); 308 | break; 309 | case 'ÿ': 310 | encoded.Append("ÿ"); 311 | break; 312 | default: 313 | if (ch > 127) 314 | encoded.Append("&#" + (int)ch + ";"); 315 | else 316 | encoded.Append(ch); 317 | break; 318 | } 319 | } 320 | return encoded.ToString(); 321 | } 322 | 323 | /// 324 | /// Converts a string to an HTML-encoded string using numeric character references. 325 | /// 326 | /// The string to encode. 327 | /// An encoded string. 328 | public static string EncodeNumeric(string source) 329 | { 330 | if (string.IsNullOrEmpty(source)) 331 | return string.Empty; 332 | 333 | var encoded = new StringBuilder(source.Length); 334 | foreach (var ch in source) 335 | { 336 | if (ch == ' ') 337 | { 338 | encoded.Append("&#"); 339 | encoded.Append(160); //   340 | encoded.Append(';'); 341 | } 342 | else if (ch > 127 || ch == '<' || ch == '>' || ch == '"' || ch == '&' || ch == '\'') 343 | { 344 | encoded.Append("&#"); 345 | encoded.Append((int)ch); 346 | encoded.Append(';'); 347 | } 348 | else 349 | { 350 | encoded.Append(ch); 351 | } 352 | } 353 | return encoded.ToString(); 354 | } 355 | 356 | public static string RemoveHtmlTags(string s, bool alsoSsaTags = false) 357 | { 358 | if (s == null || s.Length < 3) 359 | return s; 360 | 361 | if (alsoSsaTags) 362 | s = Utilities.RemoveSsaTags(s); 363 | 364 | if (!Utilities.Contains(s, '<')) 365 | return s; 366 | 367 | if (s.Contains("< ")) 368 | s = FixInvalidItalicTags(s); 369 | 370 | return RemoveOpenCloseTags(s, TagItalic, TagBold, TagUnderline, TagParagraph, TagFont, TagCyrillicI); 371 | } 372 | 373 | public static bool IsUrl(string text) 374 | { 375 | if (string.IsNullOrWhiteSpace(text) || text.Length < 6 || !Utilities.Contains(text, '.') || Utilities.Contains(text, ' ')) 376 | return false; 377 | 378 | var allLower = text.ToLower(); 379 | if (allLower.StartsWith("http://", StringComparison.Ordinal) || allLower.StartsWith("https://", StringComparison.Ordinal) || 380 | allLower.StartsWith("www.", StringComparison.Ordinal) || allLower.EndsWith(".org", StringComparison.Ordinal) || 381 | allLower.EndsWith(".com", StringComparison.Ordinal) || allLower.EndsWith(".net", StringComparison.Ordinal)) 382 | return true; 383 | 384 | if (allLower.Contains(".org/") || allLower.Contains(".com/") || allLower.Contains(".net/")) 385 | return true; 386 | 387 | return false; 388 | } 389 | 390 | public static bool StartsWithUrl(string text) 391 | { 392 | if (string.IsNullOrWhiteSpace(text)) 393 | return false; 394 | 395 | var arr = text.Trim().TrimEnd('.').TrimEnd().Split(); 396 | if (arr.Length == 0) 397 | return false; 398 | 399 | return IsUrl(arr[0]); 400 | } 401 | 402 | private static readonly string[] UppercaseTags = { "", "", "", "", "", "", "" }; 403 | 404 | public static string FixUpperTags(string text) 405 | { 406 | if (string.IsNullOrEmpty(text) || !Utilities.Contains(text, '<')) 407 | return text; 408 | var idx = text.IndexOfAny(UppercaseTags, StringComparison.Ordinal); 409 | while (idx >= 0) 410 | { 411 | var endIdx = text.IndexOf('>', idx + 2); 412 | if (endIdx < idx) 413 | break; 414 | var tag = text.Substring(idx, endIdx - idx).ToLowerInvariant(); 415 | text = text.Remove(idx, endIdx - idx).Insert(idx, tag); 416 | idx = text.IndexOfAny(UppercaseTags, StringComparison.Ordinal); 417 | } 418 | return text; 419 | } 420 | 421 | public static string FixInvalidItalicTags(string text) 422 | { 423 | const string beginTag = ""; 424 | const string endTag = ""; 425 | 426 | text = text.Replace("< i >", beginTag); 427 | text = text.Replace("< i>", beginTag); 428 | text = text.Replace("", beginTag); 429 | text = text.Replace("", beginTag); 430 | text = text.Replace("< I >", beginTag); 431 | text = text.Replace("< I>", beginTag); 432 | text = text.Replace("", beginTag); 433 | 434 | text = text.Replace("< / i >", endTag); 435 | text = text.Replace("< /i>", endTag); 436 | text = text.Replace("", endTag); 437 | text = text.Replace("< /i >", endTag); 438 | text = text.Replace("", endTag); 439 | text = text.Replace("", endTag); 440 | text = text.Replace("< / i>", endTag); 441 | text = text.Replace("", endTag); 442 | text = text.Replace("< / I >", endTag); 443 | text = text.Replace("< /I>", endTag); 444 | text = text.Replace("", endTag); 445 | text = text.Replace("< /I >", endTag); 446 | text = text.Replace("", endTag); 447 | text = text.Replace("", endTag); 448 | text = text.Replace("< / I>", endTag); 449 | 450 | text = text.Replace(" ", "_@_"); 451 | text = text.Replace(" _@_", "_@_"); 452 | text = text.Replace(" _@_ ", "_@_"); 453 | text = text.Replace("_@_", " "); 454 | 455 | if (text.Contains(beginTag)) 456 | { 457 | text = text.Replace("", endTag); 458 | text = text.Replace("", endTag); 459 | } 460 | else 461 | { 462 | text = text.Replace("", string.Empty); 463 | text = text.Replace("", string.Empty); 464 | } 465 | 466 | text = text.Replace(beginTag + beginTag, beginTag); 467 | text = text.Replace(endTag + endTag, endTag); 468 | 469 | int italicBeginTagCount = Utilities.CountTagInText(text, beginTag); 470 | int italicEndTagCount = Utilities.CountTagInText(text, endTag); 471 | int noOfLines = Utilities.GetNumberOfLines(text); 472 | if (italicBeginTagCount + italicEndTagCount > 0) 473 | { 474 | if (italicBeginTagCount == 1 && italicEndTagCount == 1 && text.IndexOf(beginTag, StringComparison.Ordinal) > text.IndexOf(endTag, StringComparison.Ordinal)) 475 | { 476 | const string pattern = "___________@"; 477 | text = text.Replace(beginTag, pattern); 478 | text = text.Replace(endTag, beginTag); 479 | text = text.Replace(pattern, endTag); 480 | } 481 | 482 | if (italicBeginTagCount == 2 && italicEndTagCount == 0) 483 | { 484 | int firstIndex = text.IndexOf(beginTag, StringComparison.Ordinal); 485 | int lastIndex = text.LastIndexOf(beginTag, StringComparison.Ordinal); 486 | int lastIndexWithNewLine = text.LastIndexOf(Environment.NewLine + beginTag, StringComparison.Ordinal) + Environment.NewLine.Length; 487 | if (noOfLines == 2 && lastIndex == lastIndexWithNewLine && firstIndex < 2) 488 | text = text.Replace(Environment.NewLine, endTag + Environment.NewLine) + endTag; 489 | else 490 | text = text.Remove(lastIndex, beginTag.Length).Insert(lastIndex, endTag); 491 | } 492 | 493 | if (italicBeginTagCount == 1 && italicEndTagCount == 2) 494 | { 495 | int firstIndex = text.IndexOf(endTag, StringComparison.Ordinal); 496 | if (text.StartsWith("--", StringComparison.Ordinal) || 497 | text.StartsWith("- -", StringComparison.Ordinal) || 498 | text.StartsWith("- -", StringComparison.Ordinal) || 499 | text.StartsWith("- -", StringComparison.Ordinal)) 500 | text = text.Remove(0, 5); 501 | else if (firstIndex == 0) 502 | text = text.Remove(0, 4); 503 | else 504 | text = text.Substring(0, firstIndex) + text.Substring(firstIndex + endTag.Length); 505 | } 506 | 507 | if (italicBeginTagCount == 2 && italicEndTagCount == 1) 508 | { 509 | var lines = text.SplitToLines(); 510 | if (lines.Length == 2 && lines[0].StartsWith(beginTag, StringComparison.Ordinal) && lines[0].EndsWith(endTag, StringComparison.Ordinal) && 511 | lines[1].StartsWith(beginTag, StringComparison.Ordinal)) 512 | { 513 | text = text.TrimEnd() + endTag; 514 | } 515 | else 516 | { 517 | int lastIndex = text.LastIndexOf(beginTag, StringComparison.Ordinal); 518 | if (text.Length > lastIndex + endTag.Length) 519 | text = text.Substring(0, lastIndex) + text.Substring(lastIndex - 1 + endTag.Length); 520 | else 521 | text = text.Substring(0, lastIndex - 1) + endTag; 522 | } 523 | if (text.StartsWith(beginTag, StringComparison.Ordinal) && text.EndsWith(endTag, StringComparison.Ordinal) && text.Contains(endTag + Environment.NewLine + beginTag)) 524 | { 525 | text = text.Replace(endTag + Environment.NewLine + beginTag, Environment.NewLine); 526 | } 527 | } 528 | 529 | if (italicBeginTagCount == 1 && italicEndTagCount == 0) 530 | { 531 | int lastIndexWithNewLine = text.LastIndexOf(Environment.NewLine + beginTag, StringComparison.Ordinal) + Environment.NewLine.Length; 532 | int lastIndex = text.LastIndexOf(beginTag, StringComparison.Ordinal); 533 | 534 | if (text.StartsWith(beginTag, StringComparison.Ordinal)) 535 | text += endTag; 536 | else if (noOfLines == 2 && lastIndex == lastIndexWithNewLine) 537 | text += endTag; 538 | else 539 | text = text.Replace(beginTag, string.Empty); 540 | } 541 | 542 | if (italicBeginTagCount == 0 && italicEndTagCount == 1) 543 | { 544 | var cleanText = RemoveOpenCloseTags(text, TagItalic, TagBold, TagUnderline, TagCyrillicI); 545 | bool isFixed = false; 546 | 547 | // Foo. 548 | if (text.EndsWith(endTag, StringComparison.Ordinal) && !cleanText.StartsWith("-") && !cleanText.Contains(Environment.NewLine + "-")) 549 | { 550 | text = beginTag + text; 551 | isFixed = true; 552 | } 553 | 554 | // - Foo | - Foo. 555 | // - Bar. | - Foo. 556 | if (!isFixed && Utilities.GetNumberOfLines(cleanText) == 2) 557 | { 558 | int newLineIndex = text.IndexOf(Environment.NewLine, StringComparison.Ordinal); 559 | if (newLineIndex > 0) 560 | { 561 | var firstLine = text.Substring(0, newLineIndex).Trim(); 562 | var secondLine = text.Substring(newLineIndex + 2).Trim(); 563 | if (firstLine.EndsWith(endTag, StringComparison.Ordinal)) 564 | { 565 | firstLine = beginTag + firstLine; 566 | isFixed = true; 567 | } 568 | if (secondLine.EndsWith(endTag, StringComparison.Ordinal)) 569 | { 570 | secondLine = beginTag + secondLine; 571 | isFixed = true; 572 | } 573 | text = firstLine + Environment.NewLine + secondLine; 574 | } 575 | } 576 | if (!isFixed) 577 | text = text.Replace(endTag, string.Empty); 578 | } 579 | 580 | // - foo. 581 | // - bar. 582 | if (italicBeginTagCount == 0 && italicEndTagCount == 2 && text.Contains(endTag + Environment.NewLine, StringComparison.Ordinal) && text.EndsWith(endTag, StringComparison.Ordinal)) 583 | { 584 | text = text.Replace(endTag, string.Empty); 585 | text = beginTag + text + endTag; 586 | } 587 | 588 | if (italicBeginTagCount == 0 && italicEndTagCount == 2 && text.StartsWith(endTag, StringComparison.Ordinal) && text.EndsWith(endTag, StringComparison.Ordinal)) 589 | { 590 | int firstIndex = text.IndexOf(endTag, StringComparison.Ordinal); 591 | text = text.Remove(firstIndex, endTag.Length).Insert(firstIndex, beginTag); 592 | } 593 | 594 | // Foo 595 | // Bar 596 | if (italicBeginTagCount == 2 && italicEndTagCount == 2 && noOfLines == 2) 597 | { 598 | int index = text.IndexOf(Environment.NewLine, StringComparison.Ordinal); 599 | if (index > 0 && text.Length > index + (beginTag.Length + endTag.Length)) 600 | { 601 | var firstLine = text.Substring(0, index).Trim(); 602 | var secondLine = text.Substring(index + Environment.NewLine.Length).Trim(); 603 | 604 | if (firstLine.Length > 10 && firstLine.StartsWith("- ", StringComparison.Ordinal) && firstLine.EndsWith(endTag, StringComparison.Ordinal)) 605 | { 606 | text = "- " + firstLine.Remove(0, 5) + Environment.NewLine + secondLine; 607 | text = text.Replace("- ", "- "); 608 | index = text.IndexOf(Environment.NewLine, StringComparison.Ordinal); 609 | firstLine = text.Substring(0, index).Trim(); 610 | secondLine = text.Substring(index + Environment.NewLine.Length).Trim(); 611 | } 612 | if (secondLine.Length > 10 && secondLine.StartsWith("- ", StringComparison.Ordinal) && secondLine.EndsWith(endTag, StringComparison.Ordinal)) 613 | { 614 | text = firstLine + Environment.NewLine + "- " + secondLine.Remove(0, 5); 615 | text = text.Replace("- ", "- "); 616 | index = text.IndexOf(Environment.NewLine, StringComparison.Ordinal); 617 | firstLine = text.Substring(0, index).Trim(); 618 | secondLine = text.Substring(index + Environment.NewLine.Length).Trim(); 619 | } 620 | 621 | if (Utilities.StartsAndEndsWithTag(firstLine, beginTag, endTag) && Utilities.StartsAndEndsWithTag(secondLine, beginTag, endTag)) 622 | { 623 | text = text.Replace(beginTag, string.Empty).Replace(endTag, string.Empty).Trim(); 624 | text = beginTag + text + endTag; 625 | } 626 | } 627 | 628 | //FALCONE: I didn't think
it was going to be you, 629 | var colIdx = text.IndexOf(':'); 630 | if (colIdx >= 0 && Utilities.CountTagInText(text, beginTag) + Utilities.CountTagInText(text, endTag) == 4 && text.Length > colIdx + 1 && !char.IsDigit(text[colIdx + 1])) 631 | { 632 | var firstLine = text.Substring(0, index); 633 | var secondLine = text.Substring(index).TrimStart(); 634 | 635 | var secIdxCol = secondLine.IndexOf(':'); 636 | if (secIdxCol < 0 || !Utilities.IsBetweenNumbers(secondLine, secIdxCol)) 637 | { 638 | var idx = firstLine.IndexOf(':'); 639 | if (idx > 1) 640 | { 641 | var pre = text.Substring(0, idx + 1).TrimStart(); 642 | text = text.Remove(0, idx + 1); 643 | text = FixInvalidItalicTags(text).Trim(); 644 | if (text.StartsWith(" ", StringComparison.OrdinalIgnoreCase)) 645 | text = Utilities.RemoveSpaceBeforeAfterTag(text, beginTag); 646 | text = pre + " " + text; 647 | } 648 | } 649 | } 650 | } 651 | 652 | //- You think they're they gone? 653 | //- That can't be. 654 | if (italicBeginTagCount == 3 && italicEndTagCount == 1 && noOfLines == 2) 655 | { 656 | var newLineIdx = text.IndexOf(Environment.NewLine, StringComparison.Ordinal); 657 | var firstLine = text.Substring(0, newLineIdx).Trim(); 658 | var secondLine = text.Substring(newLineIdx).Trim(); 659 | 660 | if ((Utilities.StartsAndEndsWithTag(firstLine, beginTag, beginTag) && Utilities.StartsAndEndsWithTag(secondLine, beginTag, endTag)) || 661 | (Utilities.StartsAndEndsWithTag(secondLine, beginTag, beginTag) && Utilities.StartsAndEndsWithTag(firstLine, beginTag, endTag))) 662 | { 663 | text = text.Replace(beginTag, string.Empty); 664 | text = text.Replace(endTag, string.Empty); 665 | text = text.Replace(" ", " ").Trim(); 666 | text = beginTag + text + endTag; 667 | } 668 | } 669 | 670 | if (noOfLines == 3) 671 | { 672 | var lines = text.SplitToLines(); 673 | if (italicBeginTagCount == 3 && italicEndTagCount == 2 || italicBeginTagCount == 2 && italicEndTagCount == 3) 674 | { 675 | int numberOfItalics = 0; 676 | foreach (var line in lines) 677 | { 678 | if (line.StartsWith(beginTag, StringComparison.Ordinal)) 679 | numberOfItalics++; 680 | if (line.EndsWith(endTag, StringComparison.Ordinal)) 681 | numberOfItalics++; 682 | } 683 | if (numberOfItalics == 5) 684 | { // fix missing tag 685 | text = "" + text.Replace("", string.Empty).Replace("", string.Empty) + ""; 686 | } 687 | } 688 | } 689 | 690 | text = text.Replace("", string.Empty); 691 | text = text.Replace(" ", string.Empty); 692 | text = text.Replace(" ", string.Empty); 693 | } 694 | return text; 695 | } 696 | 697 | public static string ToggleTag(string text, string tag) 698 | { 699 | if (text.IndexOf("<" + tag + ">", StringComparison.OrdinalIgnoreCase) >= 0 || 700 | text.IndexOf("", StringComparison.OrdinalIgnoreCase) >= 0) 701 | { 702 | text = text.Replace("<" + tag + ">", string.Empty); 703 | text = text.Replace("", string.Empty); 704 | text = text.Replace("<" + tag.ToUpper() + ">", string.Empty); 705 | text = text.Replace("", string.Empty); 706 | } 707 | else 708 | { 709 | int indexOfEndBracket = text.IndexOf('}'); 710 | if (text.StartsWith("{\\", StringComparison.Ordinal) && indexOfEndBracket > 1 && indexOfEndBracket < 6) 711 | { 712 | text = $"{text.Substring(0, indexOfEndBracket + 1)}<{tag}>{text.Remove(0, indexOfEndBracket + 1)}"; 713 | } 714 | else 715 | { 716 | text = $"<{tag}>{text}"; 717 | } 718 | } 719 | return text; 720 | } 721 | 722 | } 723 | } 724 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Translator/ITranslator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | 4 | namespace XmlContentTranslator.Translator 5 | { 6 | public interface ITranslator 7 | { 8 | List GetTranslationPairs(); 9 | string GetName(); 10 | string GetUrl(); 11 | List Translate(string sourceLanguage, string targetLanguage, List paragraphs, StringBuilder log); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Translator/Json.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace XmlContentTranslator.Translator 5 | { 6 | public class Json 7 | { 8 | public static string EncodeJsonText(string text) 9 | { 10 | var sb = new StringBuilder(text.Length); 11 | foreach (var c in text) 12 | { 13 | switch (c) 14 | { 15 | case '\\': 16 | sb.Append("\\\\"); 17 | break; 18 | case '"': 19 | sb.Append("\\\""); 20 | break; 21 | default: 22 | sb.Append(c); 23 | break; 24 | } 25 | } 26 | return sb.ToString().Replace(Environment.NewLine, "
"); 27 | } 28 | 29 | public static string DecodeJsonText(string text) 30 | { 31 | text = text.Replace("
", Environment.NewLine); 32 | text = text.Replace("
", Environment.NewLine); 33 | text = text.Replace("
", Environment.NewLine); 34 | text = text.Replace("\\n", Environment.NewLine); 35 | bool keepNext = false; 36 | var sb = new StringBuilder(text.Length); 37 | foreach (var c in text) 38 | { 39 | if (c == '\\' && !keepNext) 40 | { 41 | keepNext = true; 42 | } 43 | else 44 | { 45 | sb.Append(c); 46 | keepNext = false; 47 | } 48 | } 49 | return sb.ToString(); 50 | } 51 | 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Translator/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Text; 4 | 5 | namespace XmlContentTranslator.Translator 6 | { 7 | public static class StringExtensions 8 | { 9 | public static bool LineStartsWithHtmlTag(this string text, bool threeLengthTag, bool includeFont = false) 10 | { 11 | if (text == null || (!threeLengthTag && !includeFont)) 12 | return false; 13 | return StartsWithHtmlTag(text, threeLengthTag, includeFont); 14 | } 15 | 16 | public static bool LineEndsWithHtmlTag(this string text, bool threeLengthTag, bool includeFont = false) 17 | { 18 | if (text == null) 19 | return false; 20 | 21 | var len = text.Length; 22 | if (len < 6 || text[len - 1] != '>') 23 | return false; 24 | 25 | //
26 | if (threeLengthTag && len > 3 && text[len - 4] == '<' && text[len - 3] == '/') 27 | return true; 28 | if (includeFont && len > 8 && text[len - 7] == '<' && text[len - 6] == '/') 29 | return true; 30 | return false; 31 | } 32 | 33 | public static bool LineBreakStartsWithHtmlTag(this string text, bool threeLengthTag, bool includeFont = false) 34 | { 35 | if (text == null || (!threeLengthTag && !includeFont)) 36 | return false; 37 | var newLineIdx = text.IndexOf(Environment.NewLine, StringComparison.Ordinal); 38 | if (newLineIdx < 0 || text.Length < newLineIdx + 5) 39 | return false; 40 | text = text.Substring(newLineIdx + 2); 41 | return StartsWithHtmlTag(text, threeLengthTag, includeFont); 42 | } 43 | 44 | private static bool StartsWithHtmlTag(string text, bool threeLengthTag, bool includeFont) 45 | { 46 | if (threeLengthTag && text.Length >= 3 && text[0] == '<' && text[2] == '>' && (text[1] == 'i' || text[1] == 'I' || text[1] == 'u' || text[1] == 'U' || text[1] == 'b' || text[1] == 'B')) 47 | return true; 48 | if (includeFont && text.Length > 5 && text.StartsWith("', 5) >= 5; // or 50 | return false; 51 | } 52 | 53 | public static bool StartsWith(this string s, char c) 54 | { 55 | return s.Length > 0 && s[0] == c; 56 | } 57 | 58 | public static bool StartsWith(this StringBuilder sb, char c) 59 | { 60 | return sb.Length > 0 && sb[0] == c; 61 | } 62 | 63 | public static bool EndsWith(this string s, char c) 64 | { 65 | return s.Length > 0 && s[s.Length - 1] == c; 66 | } 67 | 68 | public static bool EndsWith(this StringBuilder sb, char c) 69 | { 70 | return sb.Length > 0 && sb[sb.Length - 1] == c; 71 | } 72 | 73 | public static bool Contains(this string source, char value) 74 | { 75 | return source.IndexOf(value) >= 0; 76 | } 77 | 78 | public static bool Contains(this string source, char[] value) 79 | { 80 | return source.IndexOfAny(value) >= 0; 81 | } 82 | 83 | public static bool Contains(this string source, string value, StringComparison comparisonType) 84 | { 85 | return source.IndexOf(value, comparisonType) >= 0; 86 | } 87 | 88 | public static string[] SplitToLines(this string source) 89 | { 90 | return source.Replace("\r\r\n", "\n").Replace("\r\n", "\n").Replace('\r', '\n').Replace('\u2028', '\n').Split('\n'); 91 | } 92 | 93 | public static int CountWords(this string source) 94 | { 95 | return HtmlUtil.RemoveHtmlTags(source, true).Split(new[] { ' ', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries).Length; 96 | } 97 | 98 | // http://www.codeproject.com/Articles/43726/Optimizing-string-operations-in-C 99 | public static int FastIndexOf(this string source, string pattern) 100 | { 101 | if (pattern == null) throw new ArgumentNullException(); 102 | if (pattern.Length == 0) return 0; 103 | if (pattern.Length == 1) return source.IndexOf(pattern[0]); 104 | int limit = source.Length - pattern.Length + 1; 105 | if (limit < 1) return -1; 106 | // Store the first 2 characters of "pattern" 107 | char c0 = pattern[0]; 108 | char c1 = pattern[1]; 109 | // Find the first occurrence of the first character 110 | int first = source.IndexOf(c0, 0, limit); 111 | while (first != -1) 112 | { 113 | // Check if the following character is the same like 114 | // the 2nd character of "pattern" 115 | if (source[first + 1] != c1) 116 | { 117 | first = source.IndexOf(c0, ++first, limit - first); 118 | continue; 119 | } 120 | // Check the rest of "pattern" (starting with the 3rd character) 121 | bool found = true; 122 | for (var j = 2; j < pattern.Length; j++) 123 | if (source[first + j] != pattern[j]) 124 | { 125 | found = false; 126 | break; 127 | } 128 | // If the whole word was found, return its index, otherwise try again 129 | if (found) return first; 130 | first = source.IndexOf(c0, ++first, limit - first); 131 | } 132 | return -1; 133 | } 134 | 135 | public static int IndexOfAny(this string s, string[] words, StringComparison comparisonType) 136 | { 137 | if (words == null || string.IsNullOrEmpty(s)) 138 | return -1; 139 | for (int i = 0; i < words.Length; i++) 140 | { 141 | var idx = s.IndexOf(words[i], comparisonType); 142 | if (idx >= 0) 143 | return idx; 144 | } 145 | return -1; 146 | } 147 | 148 | public static string FixExtraSpaces(this string s) 149 | { 150 | if (string.IsNullOrEmpty(s)) 151 | return s; 152 | int len = s.Length; 153 | int k = -1; 154 | for (int i = len - 1; i >= 0; i--) 155 | { 156 | char ch = s[i]; 157 | if (k < 2) 158 | { 159 | if (ch == 0x20) 160 | { 161 | k = i + 1; 162 | } 163 | } 164 | else if (ch != 0x20) 165 | { 166 | // Two or more white-spaces found! 167 | if (k - (i + 1) > 1) 168 | { 169 | // Keep only one white-space. 170 | s = s.Remove(i + 1, k - (i + 2)); 171 | } 172 | 173 | // No white-space after/before line break. 174 | if ((ch == '\n' || ch == '\r') && i + 1 < s.Length && s[i + 1] == 0x20) 175 | { 176 | s = s.Remove(i + 1, 1); 177 | } 178 | // Reset remove length. 179 | k = -1; 180 | } 181 | if (ch == 0x20 && i + 1 < s.Length && (s[i + 1] == '\n' || s[i + 1] == '\r')) 182 | { 183 | s = s.Remove(i, 1); 184 | } 185 | } 186 | return s; 187 | } 188 | 189 | public static bool ContainsLetter(this string s) 190 | { 191 | if (s != null) 192 | { 193 | foreach (var index in StringInfo.ParseCombiningCharacters(s)) 194 | { 195 | var uc = CharUnicodeInfo.GetUnicodeCategory(s, index); 196 | if (uc == UnicodeCategory.LowercaseLetter || uc == UnicodeCategory.UppercaseLetter || uc == UnicodeCategory.TitlecaseLetter || uc == UnicodeCategory.ModifierLetter || uc == UnicodeCategory.OtherLetter) 197 | return true; 198 | } 199 | } 200 | return false; 201 | } 202 | 203 | public static string RemoveControlCharacters(this string s) 204 | { 205 | int max = s.Length; 206 | var newStr = new char[max]; 207 | int newIdx = 0; 208 | for (int index = 0; index < max; index++) 209 | { 210 | var ch = s[index]; 211 | if (!char.IsControl(ch)) 212 | { 213 | newStr[newIdx++] = ch; 214 | } 215 | } 216 | return new string(newStr, 0, newIdx); 217 | } 218 | 219 | public static string RemoveControlCharactersButWhiteSpace(this string s) 220 | { 221 | int max = s.Length; 222 | var newStr = new char[max]; 223 | int newIdx = 0; 224 | for (int index = 0; index < max; index++) 225 | { 226 | var ch = s[index]; 227 | if (!char.IsControl(ch) || ch == '\u000d' || ch == '\u000a' || ch == '\u0009') 228 | { 229 | newStr[newIdx++] = ch; 230 | } 231 | } 232 | return new string(newStr, 0, newIdx); 233 | } 234 | 235 | public static string CapitalizeFirstLetter(this string s, CultureInfo ci = null) 236 | { 237 | var si = new StringInfo(s); 238 | if (ci == null) 239 | ci = CultureInfo.CurrentCulture; 240 | if (si.LengthInTextElements > 0) 241 | s = si.SubstringByTextElements(0, 1).ToUpper(ci); 242 | if (si.LengthInTextElements > 1) 243 | s += si.SubstringByTextElements(1); 244 | return s; 245 | } 246 | 247 | public static string ToRtf(this string value) 248 | { 249 | return @"{\rtf1\ansi\ansicpg1252\deff0{\fonttbl\f0\fswiss Helvetica;}\f0\pard " + value.ToRtfPart() + @"\par" + Environment.NewLine + "}"; 250 | } 251 | 252 | public static string ToRtfPart(this string value) 253 | { 254 | // special RTF chars 255 | var backslashed = new StringBuilder(value); 256 | backslashed.Replace(@"\", @"\\"); 257 | backslashed.Replace(@"{", @"\{"); 258 | backslashed.Replace(@"}", @"\}"); 259 | backslashed.Replace(Environment.NewLine, @"\par" + Environment.NewLine); 260 | 261 | // convert string char by char 262 | var sb = new StringBuilder(); 263 | foreach (char character in backslashed.ToString()) 264 | { 265 | if (character <= 0x7f) 266 | sb.Append(character); 267 | else 268 | sb.Append("\\u" + Convert.ToUInt32(character) + "?"); 269 | } 270 | return sb.ToString(); 271 | } 272 | 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Translator/TranslationHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | 3 | namespace XmlContentTranslator.Translator 4 | { 5 | public static class TranslationHelper 6 | { 7 | public static string PostTranslate(string s, string target) 8 | { 9 | if (target == "da") 10 | { 11 | s = s.Replace("Jeg ved.", "Jeg ved det."); 12 | s = s.Replace(", jeg ved.", ", jeg ved det."); 13 | 14 | s = s.Replace("Jeg er ked af.", "Jeg er ked af det."); 15 | s = s.Replace(", jeg er ked af.", ", jeg er ked af det."); 16 | 17 | s = s.Replace("Come on.", "Kom nu."); 18 | s = s.Replace(", come on.", ", kom nu."); 19 | s = s.Replace("Come on,", "Kom nu,"); 20 | 21 | s = s.Replace("Hey ", "Hej "); 22 | s = s.Replace("Hey,", "Hej,"); 23 | 24 | s = s.Replace(" gonna ", " ville "); 25 | s = s.Replace("Gonna ", "Vil "); 26 | 27 | s = s.Replace("Ked af.", "Undskyld."); 28 | } 29 | return s; 30 | } 31 | 32 | public static string PreTranslate(string s, string source) 33 | { 34 | if (source == "en") 35 | { 36 | s = Regex.Replace(s, @"\bI'm ", "I am "); 37 | s = Regex.Replace(s, @"\bI've ", "I have "); 38 | s = Regex.Replace(s, @"\bI'll ", "I will "); 39 | // s = Regex.Replace(s, @"\bI'd ", "I would "); // had or would??? 40 | s = Regex.Replace(s, @"\b(I|i)t's ", "$1t is "); 41 | s = Regex.Replace(s, @"\b(Y|y)ou're ", "$1ou are "); 42 | s = Regex.Replace(s, @"\b(Y|y)ou've ", "$1ou have "); 43 | s = Regex.Replace(s, @"\b(Y|y)ou'll ", "$1ou will "); 44 | // s = Regex.Replace(s, @"\b(Y|y)ou'd ", "$1ou would "); // had or would??? 45 | s = Regex.Replace(s, @"\b(H|h)e's ", "$1e is "); 46 | s = Regex.Replace(s, @"\b(S|s)he's ", "$1he is "); 47 | s = Regex.Replace(s, @"\b(W|w)e're ", "$1e are "); 48 | s = Regex.Replace(s, @"\bwon't ", "will not "); 49 | s = Regex.Replace(s, @"\bdon't ", "do not "); 50 | s = Regex.Replace(s, @"\bDon't ", "Do not "); 51 | s = Regex.Replace(s, @"\b(W|w)e're ", "$1e are "); 52 | s = Regex.Replace(s, @"\b(T|t)hey're ", "$1hey are "); 53 | s = Regex.Replace(s, @"\b(W|w)ho's ", "$1ho is "); 54 | s = Regex.Replace(s, @"\b(T|t)hat's ", "$1hat is "); 55 | s = Regex.Replace(s, @"\b(W|w)hat's ", "$1hat is "); 56 | s = Regex.Replace(s, @"\b(W|w)here's ", "$1here is "); 57 | s = Regex.Replace(s, @"\b(W|w)ho's ", "$1ho is "); 58 | s = Regex.Replace(s, @"\B'(C|c)ause ", "$1ecause "); // \b (word boundry) does not workig with ' 59 | } 60 | return s; 61 | } 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Translator/TranslationPair.cs: -------------------------------------------------------------------------------- 1 | namespace XmlContentTranslator.Translator 2 | { 3 | public class TranslationPair 4 | { 5 | public string Name { get; set; } 6 | public string Code { get; set; } 7 | 8 | public TranslationPair() 9 | { 10 | 11 | } 12 | 13 | public TranslationPair(string name, string code) 14 | { 15 | Name = name; 16 | Code = code; 17 | } 18 | 19 | public override string ToString() 20 | { 21 | return Name; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/Translator/Utilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | 7 | namespace XmlContentTranslator.Translator 8 | { 9 | public static class Utilities 10 | { 11 | 12 | public static readonly string UppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWZYXÆØÃÅÄÖÉÈÁÂÀÇÊÍÓÔÕÚŁАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯĞİŞÜÙÁÌÑÎ"; 13 | public static readonly string LowercaseLetters = UppercaseLetters.ToLower(); 14 | public static readonly string LowercaseLettersWithNumbers = LowercaseLetters + "0123456789"; 15 | public static readonly string AllLetters = UppercaseLetters + LowercaseLetters; 16 | public static readonly string AllLettersAndNumbers = UppercaseLetters + LowercaseLettersWithNumbers; 17 | 18 | //#region StringExtension 19 | public static bool Contains(this string s, char c) 20 | { 21 | return s.Length > 0 && s.IndexOf(c) > 0; 22 | } 23 | //public static string[] SplitToLines(this string s) 24 | //{ 25 | // return s.Replace(Environment.NewLine, "\n").Replace('\r', '\n').Split('\n'); 26 | //} 27 | //#endregion 28 | internal static string AssemblyVersion 29 | { 30 | get 31 | { 32 | return Assembly.GetExecutingAssembly().GetName().Version.ToString(); 33 | } 34 | } 35 | 36 | public static bool IsInteger(string s) 37 | { 38 | int i; 39 | return int.TryParse(s, out i); 40 | } 41 | 42 | public static string RemoveHtmlTags(string s, bool alsoSSA = false) 43 | { 44 | if (string.IsNullOrEmpty(s)) 45 | return string.Empty; 46 | 47 | if (alsoSSA) 48 | s = RemoveSsaTags(s); 49 | 50 | if (!Contains(s, '<')) 51 | return s; 52 | s = Regex.Replace(s, "(?i)", string.Empty); 53 | while (s.Contains(" ")) s = s.Replace(" ", " "); 54 | return RemoveHtmlFontTag(s).Trim(); 55 | } 56 | 57 | public static string RemoveSsaTags(string s) 58 | { 59 | const string tag = "{\\"; 60 | var idx = s.IndexOf(tag, StringComparison.Ordinal); 61 | while (idx >= 0) 62 | { 63 | var endIdx = s.IndexOf('}'); 64 | if (endIdx < idx) 65 | break; 66 | s = s.Remove(idx, endIdx - idx + 1); 67 | idx = s.IndexOf(tag, StringComparison.Ordinal); 68 | } 69 | return s; 70 | } 71 | 72 | public static string RemoveHtmlFontTag(string s) 73 | { 74 | s = Regex.Replace(s, "(?i)", string.Empty); 75 | var idx = s.IndexOf("= 0) 77 | { 78 | var endIdx = s.IndexOf('>', idx + 5); 79 | if (endIdx < idx) break; 80 | s = s.Remove(idx, endIdx - idx + 1); 81 | idx = s.IndexOf("= 0) 91 | { 92 | count++; 93 | index = index + tag.Length; 94 | if (index >= text.Length) 95 | return count; 96 | index = text.IndexOf(tag, index, StringComparison.Ordinal); 97 | } 98 | return count; 99 | } 100 | 101 | public static string FixExtraSpaces(this string s) 102 | { 103 | if (string.IsNullOrEmpty(s)) 104 | return s; 105 | int len = s.Length; 106 | int k = -1; 107 | for (int i = len - 1; i >= 0; i--) 108 | { 109 | char ch = s[i]; 110 | if (k < 2) 111 | { 112 | if (ch == 0x20) 113 | { 114 | k = i + 1; 115 | } 116 | } 117 | else if (ch != 0x20) 118 | { 119 | // Two or more white-spaces found! 120 | if (k - (i + 1) > 1) 121 | { 122 | // Keep only one white-space. 123 | s = s.Remove(i + 1, k - (i + 2)); 124 | } 125 | 126 | // No white-space after/before line break. 127 | if ((ch == '\n' || ch == '\r') && i + 1 < s.Length && s[i + 1] == 0x20) 128 | { 129 | s = s.Remove(i + 1, 1); 130 | } 131 | // Reset remove length. 132 | k = -1; 133 | } 134 | if (ch == 0x20 && i + 1 < s.Length && (s[i + 1] == '\n' || s[i + 1] == '\r')) 135 | { 136 | s = s.Remove(i, 1); 137 | } 138 | } 139 | return s; 140 | } 141 | 142 | public static int GetNumberOfLines(string text) 143 | { 144 | if (string.IsNullOrEmpty(text)) 145 | return 0; 146 | 147 | int lines = 1; 148 | int idx = text.IndexOf('\n'); 149 | while (idx >= 0) 150 | { 151 | lines++; 152 | idx = text.IndexOf('\n', idx + 1); 153 | } 154 | return lines; 155 | } 156 | 157 | public static bool StartsAndEndsWithTag(string text, string startTag, string endTag) 158 | { 159 | if (string.IsNullOrWhiteSpace(text)) 160 | return false; 161 | if (!text.Contains(startTag) || !text.Contains(endTag)) 162 | return false; 163 | 164 | while (text.Contains(" ")) 165 | text = text.Replace(" ", " "); 166 | 167 | var s1 = "- " + startTag; 168 | var s2 = "-" + startTag; 169 | var s3 = "- ..." + startTag; 170 | var s4 = "- " + startTag + "..."; // - ... 171 | 172 | var e1 = endTag + "."; 173 | var e2 = endTag + "!"; 174 | var e3 = endTag + "?"; 175 | var e4 = endTag + "..."; 176 | var e5 = endTag + "-"; 177 | 178 | bool isStart = false; 179 | bool isEnd = false; 180 | if (text.StartsWith(startTag, StringComparison.Ordinal) || text.StartsWith(s1, StringComparison.Ordinal) || text.StartsWith(s2, StringComparison.Ordinal) || text.StartsWith(s3, StringComparison.Ordinal) || text.StartsWith(s4, StringComparison.Ordinal)) 181 | isStart = true; 182 | if (text.EndsWith(endTag, StringComparison.Ordinal) || text.EndsWith(e1, StringComparison.Ordinal) || text.EndsWith(e2, StringComparison.Ordinal) || text.EndsWith(e3, StringComparison.Ordinal) || text.EndsWith(e4, StringComparison.Ordinal) || text.EndsWith(e5, StringComparison.Ordinal)) 183 | isEnd = true; 184 | return isStart && isEnd; 185 | } 186 | 187 | public static bool IsBetweenNumbers(string s, int position) 188 | { 189 | if (string.IsNullOrEmpty(s) || position < 1 || position + 2 > s.Length) 190 | return false; 191 | return char.IsDigit(s[position - 1]) && char.IsDigit(s[position + 1]); 192 | } 193 | 194 | public static string RemoveSpaceBeforeAfterTag(string text, string openTag) 195 | { 196 | text = HtmlUtil.FixUpperTags(text); 197 | var closeTag = string.Empty; 198 | switch (openTag) 199 | { 200 | case "": 201 | closeTag = ""; 202 | break; 203 | case "": 204 | closeTag = ""; 205 | break; 206 | case "": 207 | closeTag = ""; 208 | break; 209 | } 210 | 211 | if (closeTag.Length == 0 && openTag.Contains("
Foobar 239 | if (text.StartsWith(open1, StringComparison.Ordinal)) 240 | text = openTag + text.Substring(open1.Length); 241 | 242 | // e.g.: \r\n 243 | if (text.StartsWith(open3, StringComparison.Ordinal)) 244 | text = text.Remove(openTag.Length, Environment.NewLine.Length); 245 | 246 | // e.g.: \r\n 247 | if (text.EndsWith(close5, StringComparison.Ordinal)) 248 | text = text.Remove(text.Length - openTag.Length - Environment.NewLine.Length - 1, Environment.NewLine.Length); 249 | 250 | if (text.Contains(open2, StringComparison.Ordinal)) 251 | text = text.Replace(open2, Environment.NewLine + openTag); 252 | 253 | // Hi bad man! -> Hi bad man! 254 | text = text.Replace(" " + openTag + " ", " " + openTag); 255 | text = text.Replace(Environment.NewLine + openTag + " ", Environment.NewLine + openTag); 256 | 257 | // Hi bad man! -> Hi bad man! 258 | text = text.Replace(" " + closeTag + " ", closeTag + " "); 259 | text = text.Replace(" " + closeTag + Environment.NewLine, closeTag + Environment.NewLine); 260 | 261 | text = text.Trim(); 262 | if (text.StartsWith(open1, StringComparison.Ordinal)) 263 | text = openTag + text.Substring(open1.Length); 264 | 265 | return text; 266 | } 267 | 268 | 269 | 270 | public static string AutoBreakLine(string text, string language) 271 | { 272 | return AutoBreakLine(text, 43, 22, language); 273 | } 274 | 275 | public static string AutoBreakLine(string text) 276 | { 277 | return AutoBreakLine(text, string.Empty); // no language 278 | } 279 | 280 | private static bool CanBreak(string s, int index, string language) 281 | { 282 | char nextChar; 283 | if (index >= 0 && index < s.Length) 284 | nextChar = s[index]; 285 | else 286 | return false; 287 | if (!Contains("\r\n\t ", nextChar)) 288 | return false; 289 | 290 | // Some words we don't like breaking after 291 | string s2 = s.Substring(0, index); 292 | if (s2.EndsWith("? -", StringComparison.Ordinal) || s2.EndsWith("! -", StringComparison.Ordinal) || s2.EndsWith(". -", StringComparison.Ordinal)) 293 | return false; 294 | 295 | return true; 296 | } 297 | 298 | public static string AutoBreakLineMoreThanTwoLines(string text, int maximumLineLength, string language) 299 | { 300 | if (text == null || text.Length < 3) 301 | return text; 302 | 303 | string s = AutoBreakLine(text, 0, 0, language); 304 | 305 | var arr = s.SplitToLines(); 306 | if ((arr.Length < 2 && arr[0].Length <= maximumLineLength) || (arr[0].Length <= maximumLineLength && arr[1].Length <= maximumLineLength)) 307 | return s; 308 | 309 | s = RemoveLineBreaks(s); 310 | 311 | var htmlTags = new Dictionary(); 312 | var sb = new StringBuilder(s.Length); 313 | int six = 0; 314 | while (six < s.Length) 315 | { 316 | var letter = s[six]; 317 | var tagFound = letter == '<' && (s.Substring(six).StartsWith("', six + 1); 328 | 329 | if (tagFound && endIndex > 0) 330 | { 331 | string tag = s.Substring(six, endIndex - six + 1); 332 | s = s.Remove(six, tag.Length); 333 | if (htmlTags.ContainsKey(six)) 334 | htmlTags[six] = htmlTags[six] + tag; 335 | else 336 | htmlTags.Add(six, tag); 337 | } 338 | else 339 | { 340 | sb.Append(letter); 341 | six++; 342 | } 343 | } 344 | s = sb.ToString(); 345 | 346 | var words = s.Split(' '); 347 | for (int numberOfLines = 3; numberOfLines < 9999; numberOfLines++) 348 | { 349 | int average = s.Length / numberOfLines + 1; 350 | for (int len = average; len < maximumLineLength; len++) 351 | { 352 | List list = SplitToX(words, numberOfLines, len); 353 | bool allOk = true; 354 | foreach (var lineLength in list) 355 | { 356 | if (lineLength > maximumLineLength) 357 | allOk = false; 358 | } 359 | if (allOk) 360 | { 361 | int index = 0; 362 | foreach (var item in list) 363 | { 364 | index += item; 365 | htmlTags.Add(index, Environment.NewLine); 366 | } 367 | s = ReInsertHtmlTags(s, htmlTags); 368 | s = s.Replace(" " + Environment.NewLine, Environment.NewLine); 369 | s = s.Replace(Environment.NewLine + " ", Environment.NewLine); 370 | s = s.Replace(Environment.NewLine + "
", "" + Environment.NewLine); 371 | s = s.Replace(Environment.NewLine + "", "" + Environment.NewLine); 372 | s = s.Replace(Environment.NewLine + "", "" + Environment.NewLine); 373 | s = s.Replace(Environment.NewLine + "", "
" + Environment.NewLine); 374 | return s.TrimEnd(); 375 | } 376 | } 377 | } 378 | 379 | return text; 380 | } 381 | 382 | private static List SplitToX(string[] words, int count, int average) 383 | { 384 | var list = new List(); 385 | int currentIdx = 0; 386 | int currentCount = 0; 387 | foreach (string word in words) 388 | { 389 | if (currentCount + word.Length + 3 > average && currentIdx < count) 390 | { 391 | list.Add(currentCount); 392 | currentIdx++; 393 | currentCount = 0; 394 | } 395 | currentCount += word.Length + 1; 396 | } 397 | if (currentIdx < count) 398 | list.Add(currentCount); 399 | else 400 | list[list.Count - 1] += currentCount; 401 | return list; 402 | } 403 | 404 | public static string AutoBreakLine(string text, int maximumLength, int mergeLinesShorterThan, string language) 405 | { 406 | if (text == null || text.Length < 3) 407 | return text; 408 | 409 | // do not autobreak dialogs 410 | if (Contains(text, '-') && text.Contains(Environment.NewLine)) 411 | { 412 | var noTagLines = HtmlUtil.RemoveHtmlTags(text, true).SplitToLines(); 413 | if (noTagLines.Length == 2) 414 | { 415 | var arr0 = noTagLines[0].Trim().TrimEnd('"', '\'').TrimEnd(); 416 | if (arr0.StartsWith('-') && noTagLines[1].TrimStart().StartsWith('-') && arr0.Length > 1 && (Contains(".?!)]", arr0[arr0.Length - 1]) || arr0.EndsWith("--", StringComparison.Ordinal) || arr0.EndsWith('–'))) 417 | return text; 418 | } 419 | } 420 | 421 | string s = RemoveLineBreaks(text); 422 | if (HtmlUtil.RemoveHtmlTags(s, true).Length < mergeLinesShorterThan) 423 | { 424 | return s; 425 | } 426 | 427 | var htmlTags = new Dictionary(); 428 | var sb = new StringBuilder(); 429 | int six = 0; 430 | while (six < s.Length) 431 | { 432 | var letter = s[six]; 433 | bool tagFound = false; 434 | if (letter == '<') 435 | { 436 | string tagString = s.Substring(six); 437 | tagFound = tagString.StartsWith("', six + 1); 450 | 451 | if (tagFound && endIndex > 0) 452 | { 453 | string tag = s.Substring(six, endIndex - six + 1); 454 | s = s.Remove(six, tag.Length); 455 | if (htmlTags.ContainsKey(six)) 456 | htmlTags[six] = htmlTags[six] + tag; 457 | else 458 | htmlTags.Add(six, tag); 459 | } 460 | else 461 | { 462 | sb.Append(letter); 463 | six++; 464 | } 465 | } 466 | s = sb.ToString(); 467 | 468 | int splitPos = -1; 469 | int mid = s.Length / 2; 470 | 471 | // try to find " - " with uppercase letter after (dialog) 472 | if (s.Contains(" - ")) 473 | { 474 | for (int j = 0; j <= (maximumLength / 2) + 5; j++) 475 | { 476 | if (mid + j + 4 < s.Length) 477 | { 478 | if (s[mid + j] == '-' && s[mid + j + 1] == ' ' && s[mid + j - 1] == ' ') 479 | { 480 | string rest = s.Substring(mid + j + 1).TrimStart(); 481 | if (rest.Length > 0 && char.IsUpper(rest[0])) 482 | { 483 | splitPos = mid + j; 484 | break; 485 | } 486 | } 487 | } 488 | if (mid - (j + 1) > 4) 489 | { 490 | if (s[mid - j] == '-' && s[mid - j + 1] == ' ' && s[mid - j - 1] == ' ') 491 | { 492 | string rest = s.Substring(mid - j + 1).TrimStart(); 493 | if (rest.Length > 0 && char.IsUpper(rest[0])) 494 | { 495 | if (mid - j > 5 && s[mid - j - 1] == ' ') 496 | { 497 | if (Contains("!?.", s[mid - j - 2])) 498 | { 499 | splitPos = mid - j; 500 | break; 501 | } 502 | var first = s.Substring(0, mid - j - 1); 503 | if (first.EndsWith(".\"", StringComparison.Ordinal) || first.EndsWith("!\"", StringComparison.Ordinal) || first.EndsWith("?\"", StringComparison.Ordinal)) 504 | { 505 | splitPos = mid - j; 506 | break; 507 | } 508 | } 509 | } 510 | } 511 | } 512 | } 513 | } 514 | 515 | if (splitPos == maximumLength + 1 && s[maximumLength] != ' ') // only allow space for last char (as it does not count) 516 | splitPos = -1; 517 | 518 | if (splitPos < 0) 519 | { 520 | const string expectedChars1 = ".!?0123456789"; 521 | const string expectedChars2 = ".!?"; 522 | for (int j = 0; j < 15; j++) 523 | { 524 | if (mid + j + 1 < s.Length && mid + j > 0) 525 | { 526 | if (Contains(expectedChars2, s[mid + j]) && !IsPartOfNumber(s, mid + j) && CanBreak(s, mid + j + 1, language)) 527 | { 528 | splitPos = mid + j + 1; 529 | if (Contains(expectedChars1, s[splitPos])) 530 | { // do not break double/tripple end lines like "!!!" or "..." 531 | splitPos++; 532 | if (Contains(expectedChars1, s[mid + j + 1])) 533 | splitPos++; 534 | } 535 | break; 536 | } 537 | if (Contains(expectedChars2, s[mid - j]) && !IsPartOfNumber(s, mid - j) && CanBreak(s, mid - j, language)) 538 | { 539 | splitPos = mid - j; 540 | splitPos++; 541 | break; 542 | } 543 | } 544 | } 545 | } 546 | 547 | if (splitPos > maximumLength) // too long first line 548 | { 549 | if (splitPos != maximumLength + 1 || s[maximumLength] != ' ') // allow for maxlength+1 char to be space (does not count) 550 | splitPos = -1; 551 | } 552 | else if (splitPos >= 0 && s.Length - splitPos > maximumLength) // too long second line 553 | { 554 | splitPos = -1; 555 | } 556 | 557 | if (splitPos < 0) 558 | { 559 | const string expectedChars1 = ".!?, "; 560 | const string expectedChars2 = " .!?"; 561 | const string expectedChars3 = ".!?"; 562 | for (int j = 0; j < 25; j++) 563 | { 564 | if (mid + j + 1 < s.Length && mid + j > 0) 565 | { 566 | if (Contains(expectedChars1, s[mid + j]) && !IsPartOfNumber(s, mid + j) && s.Length > mid + j + 2 && CanBreak(s, mid + j, language)) 567 | { 568 | splitPos = mid + j; 569 | if (Contains(expectedChars2, s[mid + j + 1])) 570 | { 571 | splitPos++; 572 | if (Contains(expectedChars2, s[mid + j + 2])) 573 | splitPos++; 574 | } 575 | break; 576 | } 577 | if (Contains(expectedChars1, s[mid - j]) && !IsPartOfNumber(s, mid - j) && s.Length > mid + j + 2 && CanBreak(s, mid - j, language)) 578 | { 579 | splitPos = mid - j; 580 | if (Contains(expectedChars3, s[splitPos])) 581 | splitPos--; 582 | if (Contains(expectedChars3, s[splitPos])) 583 | splitPos--; 584 | if (Contains(expectedChars3, s[splitPos])) 585 | splitPos--; 586 | break; 587 | } 588 | } 589 | } 590 | } 591 | 592 | if (splitPos < 0) 593 | { 594 | splitPos = mid; 595 | s = s.Insert(mid - 1, Environment.NewLine); 596 | s = ReInsertHtmlTags(s, htmlTags); 597 | htmlTags = new Dictionary(); 598 | s = s.Replace(Environment.NewLine, "-"); 599 | } 600 | if (splitPos < s.Length - 2) 601 | s = s.Substring(0, splitPos) + Environment.NewLine + s.Substring(splitPos); 602 | 603 | s = ReInsertHtmlTags(s, htmlTags); 604 | var idx = s.IndexOf(Environment.NewLine + " 2) 606 | { 607 | var endIdx = s.IndexOf('>', idx + 2); 608 | if (endIdx > idx) 609 | { 610 | var tag = s.Substring(idx + Environment.NewLine.Length, endIdx - (idx + Environment.NewLine.Length) + 1); 611 | s = s.Insert(idx, tag); 612 | s = s.Remove(idx + tag.Length + Environment.NewLine.Length, tag.Length); 613 | } 614 | } 615 | s = s.Replace(" " + Environment.NewLine, Environment.NewLine); 616 | s = s.Replace(Environment.NewLine + " ", Environment.NewLine); 617 | return s.TrimEnd(); 618 | } 619 | 620 | public static string RemoveLineBreaks(string s) 621 | { 622 | s = HtmlUtil.FixUpperTags(s); 623 | s = s.Replace(Environment.NewLine + "", "" + Environment.NewLine); 624 | s = s.Replace(Environment.NewLine + "", "" + Environment.NewLine); 625 | s = s.Replace(Environment.NewLine + "", "" + Environment.NewLine); 626 | s = s.Replace(Environment.NewLine + "
", "" + Environment.NewLine); 627 | s = s.Replace(" " + Environment.NewLine + "", " "); 628 | s = s.Replace("" + Environment.NewLine + " ", " "); 629 | s = s.Replace("" + Environment.NewLine + "", " "); 630 | s = s.Replace(Environment.NewLine, " "); 631 | s = s.Replace(" ", " "); 632 | s = s.Replace(" ", " "); 633 | s = s.Replace(" ", " "); 634 | s = s.Replace(" ", " "); 635 | s = FixExtraSpaces(s); 636 | return s.Trim(); 637 | } 638 | 639 | private static string ReInsertHtmlTags(string s, Dictionary htmlTags) 640 | { 641 | if (htmlTags.Count > 0) 642 | { 643 | var sb = new StringBuilder(s.Length); 644 | int six = 0; 645 | foreach (var letter in s) 646 | { 647 | if (Contains(Environment.NewLine, letter)) 648 | { 649 | sb.Append(letter); 650 | } 651 | else 652 | { 653 | if (htmlTags.ContainsKey(six)) 654 | { 655 | sb.Append(htmlTags[six]); 656 | } 657 | sb.Append(letter); 658 | six++; 659 | } 660 | } 661 | if (htmlTags.ContainsKey(six)) 662 | { 663 | sb.Append(htmlTags[six]); 664 | } 665 | return sb.ToString(); 666 | } 667 | return s; 668 | } 669 | 670 | private static bool IsPartOfNumber(string s, int position) 671 | { 672 | if (string.IsNullOrWhiteSpace(s) || position + 1 >= s.Length) 673 | return false; 674 | 675 | if (position > 0 && Contains(@",.", s[position])) 676 | { 677 | return char.IsDigit(s[position - 1]) && char.IsDigit(s[position + 1]); 678 | } 679 | return false; 680 | } 681 | 682 | } 683 | } -------------------------------------------------------------------------------- /src/XmlContentTranslator/XmlContentTranslator.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 9.0.21022 7 | 2.0 8 | {79BA7CB6-5613-4527-89C3-A4FF42810390} 9 | WinExe 10 | Properties 11 | XmlContentTranslator 12 | XmlContentTranslator 13 | v4.8 14 | 512 15 | xct2.ico 16 | 17 | 18 | 19 | 20 | 3.5 21 | 22 | 23 | 24 | true 25 | full 26 | false 27 | bin\Debug\ 28 | DEBUG;TRACE 29 | prompt 30 | 4 31 | false 32 | 33 | 34 | pdbonly 35 | true 36 | bin\Release\ 37 | TRACE 38 | prompt 39 | 4 40 | false 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Form 54 | 55 | 56 | Find.cs 57 | 58 | 59 | 60 | Form 61 | 62 | 63 | Main.cs 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | Find.cs 81 | 82 | 83 | Main.cs 84 | Designer 85 | 86 | 87 | ResXFileCodeGenerator 88 | Resources.Designer.cs 89 | Designer 90 | 91 | 92 | True 93 | Resources.resx 94 | True 95 | 96 | 97 | 98 | SettingsSingleFileGenerator 99 | Settings.Designer.cs 100 | 101 | 102 | True 103 | Settings.settings 104 | True 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 120 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/XmlContentTranslator.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | ProjectFiles 5 | 6 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/XmlUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Xml; 6 | 7 | namespace XmlContentTranslator 8 | { 9 | public static class XmlUtils 10 | { 11 | private static readonly StringBuilder Sb = new StringBuilder(); 12 | 13 | public static bool ContainsText(XmlNode node) 14 | { 15 | foreach (XmlNode childNode in node.ChildNodes) 16 | { 17 | if (childNode.NodeType == XmlNodeType.Text) 18 | { 19 | return true; 20 | } 21 | } 22 | 23 | return false; 24 | } 25 | 26 | public static bool IsTextNode(XmlNode node) 27 | { 28 | if (node.ChildNodes.Count == 1 && node.ChildNodes[0].NodeType == XmlNodeType.Text) 29 | { 30 | return true; 31 | } 32 | 33 | return ContainsText(node); 34 | } 35 | 36 | public static string BuildNodePath(XmlNode node) 37 | { 38 | Sb.Clear(); 39 | Sb.Append(node.Name); 40 | if (node.NodeType == XmlNodeType.Attribute) 41 | { 42 | XmlNode old = node; 43 | node = (node as XmlAttribute).OwnerElement; // use OwnerElement for attributes as ParentNode is null 44 | Sb.Insert(0, node.Name + "@" + GetAttributeIndex(node, old)); 45 | // node = node.ParentNode; 46 | } 47 | while (node.ParentNode != null) 48 | { 49 | Sb.Insert(0, node.ParentNode.Name + GetNodeIndex(node) + "/"); 50 | node = node.ParentNode; 51 | } 52 | return Sb.ToString(); 53 | } 54 | 55 | public static string GetAttributeIndex(XmlNode node, XmlNode child) 56 | { 57 | if (node.Attributes == null) 58 | return string.Empty; 59 | 60 | int nameCount = 0; 61 | foreach (XmlAttribute x in node.Attributes) 62 | { 63 | if (x.Name == child.Name) 64 | nameCount++; 65 | } 66 | 67 | int i = 0; 68 | foreach (XmlAttribute x in node.Attributes) 69 | { 70 | if (x == node) 71 | break; 72 | i++; 73 | } 74 | if (i == 0 || nameCount < 2) 75 | return string.Empty; 76 | return string.Format("[{0}]", i); 77 | } 78 | 79 | public static string GetNodeIndex(XmlNode node) 80 | { 81 | int i = 0; 82 | if (node.NodeType == XmlNodeType.Comment || node.NodeType == XmlNodeType.CDATA) 83 | return string.Empty; 84 | 85 | if (!string.IsNullOrEmpty(node.NamespaceURI)) 86 | { 87 | var man = new XmlNamespaceManager(node.OwnerDocument.NameTable); 88 | man.AddNamespace(node.Prefix, node.NamespaceURI); 89 | 90 | foreach (var x in node.ParentNode.SelectNodes(node.Name, man)) 91 | { 92 | if (x == node) 93 | break; 94 | i++; 95 | } 96 | 97 | } 98 | else if (node.NodeType != XmlNodeType.Text) 99 | { 100 | foreach (var x in node.ParentNode.SelectNodes(node.Name)) 101 | { 102 | if (x == node) 103 | break; 104 | i++; 105 | } 106 | } 107 | 108 | if (i == 0) 109 | return string.Empty; 110 | return string.Format("[{0}]", i); 111 | } 112 | 113 | public static bool IsParentElement(XmlNode xnode) 114 | { 115 | return xnode.ChildNodes.Count > 0 && !IsTextNode(xnode) && 116 | xnode.NodeType != XmlNodeType.Comment && xnode.NodeType != XmlNodeType.CDATA; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/XmlContentTranslator/file.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubtitleEdit/XmlContentTranslator/1941f23b4abfbe64956f34b6b7b1c9e9f1f1c43a/src/XmlContentTranslator/file.ico -------------------------------------------------------------------------------- /src/XmlContentTranslator/xct.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubtitleEdit/XmlContentTranslator/1941f23b4abfbe64956f34b6b7b1c9e9f1f1c43a/src/XmlContentTranslator/xct.ico -------------------------------------------------------------------------------- /src/XmlContentTranslator/xct2.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SubtitleEdit/XmlContentTranslator/1941f23b4abfbe64956f34b6b7b1c9e9f1f1c43a/src/XmlContentTranslator/xct2.ico --------------------------------------------------------------------------------