[gnome-subtitles] Added initial support for automatic translation, based on a patch from David Krkoska (bug #595084).



commit 082d0a19fd6d17a1fa157e915220fe4dbc960ce2
Author: Pedro Castro <mail pedrocastro org>
Date:   Sun Apr 10 22:25:49 2011 +0100

    Added initial support for automatic translation, based on a patch from David Krkoska (bug #595084). Currently only 1 subtitle can be translated at a time, and Google Translation is used.

 gnome-subtitles.mdp                                |    4 +
 src/Glade/MainWindow.glade                         |   49 ++++++--
 .../Core/Command/TranslatorCommand.cs              |  108 ++++++++++++++++
 src/GnomeSubtitles/Core/EventHandlers.cs           |   10 ++-
 src/GnomeSubtitles/Ui/Menus.cs                     |    8 +-
 src/GnomeSubtitles/Ui/WidgetNames.cs               |    2 +
 src/SubLib/Core/Translator.cs                      |  133 ++++++++++++++++++++
 src/SubLib/Exceptions/TranslatorException.cs       |   36 ++++++
 8 files changed, 336 insertions(+), 14 deletions(-)
---
diff --git a/gnome-subtitles.mdp b/gnome-subtitles.mdp
index 4a1bed8..e22ffb8 100644
--- a/gnome-subtitles.mdp
+++ b/gnome-subtitles.mdp
@@ -251,6 +251,9 @@
     <File subtype="Code" buildaction="Compile" name="src/GnomeSubtitles/Ui/VideoPreview/SubtitleTracker.cs" />
     <File subtype="Code" buildaction="Compile" name="src/GnomeSubtitles/Core/Backup.cs" />
     <File subtype="Code" buildaction="Compile" name="src/SubLib/IO/SubtitleFormats/SubtitleFormatFABSubtitler.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/GnomeSubtitles/Core/Command/TranslatorCommand.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/SubLib/Exceptions/TranslatorException.cs" />
+    <File subtype="Code" buildaction="Compile" name="src/SubLib/Core/Translator.cs" />
   </Contents>
   <References>
     <ProjectReference type="Gac" localcopy="True" refto="Mono.Posix, Version=2.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756" />
@@ -261,6 +264,7 @@
     <ProjectReference type="Gac" localcopy="True" refto="glade-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
     <ProjectReference type="Gac" localcopy="True" refto="gconf-sharp, Version=2.24.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
     <ProjectReference type="Gac" localcopy="True" refto="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
+    <ProjectReference type="Gac" localcopy="True" refto="System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
   </References>
   <LanguageParameters StartupObject="GnomeSubtitles.Execution.Executable" ApplicationIcon="." ctype="CSharpProjectParameters" />
   <DeploymentInformation strategy="File" />
diff --git a/src/Glade/MainWindow.glade b/src/Glade/MainWindow.glade
index 545a245..f38a59f 100644
--- a/src/Glade/MainWindow.glade
+++ b/src/Glade/MainWindow.glade
@@ -1,4 +1,4 @@
-<?xml version="1.0"?>
+<?xml version="1.0" encoding="UTF-8"?>
 <glade-interface>
   <!-- interface-requires gtk+ 2.16 -->
   <!-- interface-naming-policy toplevel-contextual -->
@@ -248,8 +248,8 @@
                         <property name="use_underline">True</property>
                         <property name="use_stock">True</property>
                         <signal name="activate" handler="OnEditRedo"/>
-                        <accelerator key="Y" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                         <accelerator key="Z" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
+                        <accelerator key="Y" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                       </widget>
                     </child>
                     <child>
@@ -544,8 +544,8 @@
                         <property name="label" translatable="yes">Find Ne_xt</property>
                         <property name="use_underline">True</property>
                         <signal name="activate" handler="OnSearchFindNext"/>
-                        <accelerator key="F3" signal="activate"/>
                         <accelerator key="g" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+                        <accelerator key="F3" signal="activate"/>
                       </widget>
                     </child>
                     <child>
@@ -555,8 +555,8 @@
                         <property name="label" translatable="yes">Find Pre_vious</property>
                         <property name="use_underline">True</property>
                         <signal name="activate" handler="OnSearchFindPrevious"/>
-                        <accelerator key="F3" signal="activate" modifiers="GDK_SHIFT_MASK"/>
                         <accelerator key="g" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
+                        <accelerator key="F3" signal="activate" modifiers="GDK_SHIFT_MASK"/>
                       </widget>
                     </child>
                     <child>
@@ -800,8 +800,8 @@
                         <property name="use_underline">True</property>
                         <property name="use_stock">False</property>
                         <signal name="activate" handler="OnVideoPlayPause"/>
-                        <accelerator key="F5" signal="activate"/>
                         <accelerator key="p" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+                        <accelerator key="F5" signal="activate"/>
                         <child internal-child="image">
                           <widget class="GtkImage" id="videoPlayPauseImage">
                             <property name="visible">True</property>
@@ -828,8 +828,8 @@
                         <property name="use_underline">True</property>
                         <property name="use_stock">False</property>
                         <signal name="activate" handler="OnVideoRewind"/>
-                        <accelerator key="F6" signal="activate"/>
                         <accelerator key="k" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+                        <accelerator key="F6" signal="activate"/>
                         <child internal-child="image">
                           <widget class="GtkImage" id="videoRewindImage">
                             <property name="visible">True</property>
@@ -847,8 +847,8 @@
                         <property name="use_underline">True</property>
                         <property name="use_stock">False</property>
                         <signal name="activate" handler="OnVideoForward"/>
-                        <accelerator key="F7" signal="activate"/>
                         <accelerator key="l" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+                        <accelerator key="F7" signal="activate"/>
                         <child internal-child="image">
                           <widget class="GtkImage" id="videoForwardImage">
                             <property name="visible">True</property>
@@ -888,8 +888,8 @@
                         <property name="label" translatable="yes">Seek _to Selection</property>
                         <property name="use_underline">True</property>
                         <signal name="activate" handler="OnVideoSeekToSelection"/>
-                        <accelerator key="F4" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
                         <accelerator key="r" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
+                        <accelerator key="F4" signal="activate" modifiers="GDK_SHIFT_MASK | GDK_CONTROL_MASK"/>
                       </widget>
                     </child>
                     <child>
@@ -899,8 +899,8 @@
                         <property name="label" translatable="yes">Select Nearest Subtitle</property>
                         <property name="use_underline">True</property>
                         <signal name="activate" handler="OnVideoSelectNearestSubtitle"/>
-                        <accelerator key="r" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                         <accelerator key="F4" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+                        <accelerator key="r" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                       </widget>
                     </child>
                     <child>
@@ -909,8 +909,8 @@
                         <property name="label" translatable="yes">Auto Select Subtitle</property>
                         <property name="use_underline">True</property>
                         <signal name="toggled" handler="OnVideoAutoSelectSubtitles"/>
-                        <accelerator key="j" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                         <accelerator key="F3" signal="activate" modifiers="GDK_CONTROL_MASK"/>
+                        <accelerator key="j" signal="activate" modifiers="GDK_CONTROL_MASK"/>
                       </widget>
                     </child>
                     <child>
@@ -967,7 +967,7 @@
                         <property name="visible">True</property>
                         <property name="sensitive">False</property>
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="label" translatable="yes">Set _Text Language</property>
+                        <property name="label" translatable="yes">Set T_ext Language</property>
                         <property name="use_underline">True</property>
                         <signal name="activate" handler="OnToolsSetTextLanguage"/>
                       </widget>
@@ -977,11 +977,36 @@
                         <property name="visible">True</property>
                         <property name="sensitive">False</property>
                         <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-                        <property name="label" translatable="yes">Set Translatio_n Language</property>
+                        <property name="label" translatable="yes">Set Translati_on Language</property>
                         <property name="use_underline">True</property>
                         <signal name="activate" handler="OnToolsSetTranslationLanguage"/>
                       </widget>
                     </child>
+                    <child>
+                      <widget class="GtkSeparatorMenuItem" id="toolsSeparator1">
+                        <property name="visible">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkMenuItem" id="toolsTranslateText">
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="label" translatable="yes">Translate _Text â?? Translation</property>
+                        <property name="use_underline">True</property>
+                        <signal name="activate" handler="OnToolsTranslateText"/>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkMenuItem" id="toolsTranslateTranslation">
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="label" translatable="yes">Translate Translatio_n â?? Text</property>
+                        <property name="use_underline">True</property>
+                        <signal name="activate" handler="OnToolsTranslateTranslation"/>
+                      </widget>
+                    </child>
                   </widget>
                 </child>
               </widget>
diff --git a/src/GnomeSubtitles/Core/Command/TranslatorCommand.cs b/src/GnomeSubtitles/Core/Command/TranslatorCommand.cs
new file mode 100644
index 0000000..889c481
--- /dev/null
+++ b/src/GnomeSubtitles/Core/Command/TranslatorCommand.cs
@@ -0,0 +1,108 @@
+/*
+ * This file is part of Gnome Subtitles.
+ * Copyright (C) 2011 Pedro Castro
+ *
+ * Gnome Subtitles is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Gnome Subtitles is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using GnomeSubtitles.Dialog;
+using GnomeSubtitles.Dialog.Unmanaged;
+using GnomeSubtitles.Ui.Edit;
+using Gtk;
+using Mono.Unix;
+using SubLib;
+using SubLib.Core;
+using SubLib.Core.Domain;
+using SubLib.Exceptions;
+using System;
+
+namespace GnomeSubtitles.Core.Command {
+
+public class TranslatorCommand : FixedSingleSelectionCommand {
+	private string previousValue = null; // initially, it's the former text replaced by translation
+	private bool leftToRight = true; // direction of translation
+	private static string description = Catalog.GetString("Translating");
+		
+	public TranslatorCommand (bool leftToRight) : base(description, false, true) {
+		this.leftToRight = leftToRight;
+	}
+	
+	/* Public members */
+		
+	protected override bool ChangeValues () {
+		/* If previousText is null, it's the first time the command is executed, so do translation.
+		   Otherwise, it's undo or redo, so we only need to swap the values */
+		if (previousValue == null)
+			return DoTranslation();
+		else {
+			Subtitle subtitle = Base.Document.Subtitles[Path];
+			if (leftToRight) {
+				string currentValue = subtitle.Translation.Get();
+				subtitle.Translation.Set(this.previousValue);
+				this.previousValue = currentValue;
+			}
+			else {
+				string currentValue = subtitle.Text.Get();
+				subtitle.Text.Set(this.previousValue);
+				this.previousValue = currentValue;
+			}
+			return true;
+		}
+	}
+	
+	private bool DoTranslation () {
+		if (Base.Ui.View.Selection.Count != 1) //TODO: for now, only works if 1 subtitle is selected
+			return false;
+		
+		/* Show language selection dialogs if no languages are selected */
+		if (!Base.SpellLanguages.HasActiveTextLanguage) {
+			Base.Dialogs.Get(typeof(SetTextLanguageDialog)).Show();
+			if (!Base.SpellLanguages.HasActiveTextLanguage)
+				return false;
+		}
+		
+		if (!Base.SpellLanguages.HasActiveTranslationLanguage) {
+			Base.Dialogs.Get(typeof(SetTranslationLanguageDialog)).Show();
+			if (!Base.SpellLanguages.HasActiveTranslationLanguage)
+				return false;
+		}
+			
+		try {
+			Subtitle subtitle = Base.Document.Subtitles[Path];
+			if (leftToRight) {
+				this.previousValue = subtitle.Translation.Get();
+				string translatedText = Translator.TranslateText(subtitle.Text.Get(), Base.SpellLanguages.ActiveTextLanguage.ID, Base.SpellLanguages.ActiveTranslationLanguage.ID, Translator.TIMEOUT);
+				subtitle.Translation.Set(translatedText);
+			}
+			else {
+				this.previousValue = subtitle.Text.Get();
+				string translatedText = Translator.TranslateText(subtitle.Translation.Get(), Base.SpellLanguages.ActiveTranslationLanguage.ID, Base.SpellLanguages.ActiveTextLanguage.ID, Translator.TIMEOUT);
+				subtitle.Text.Set(translatedText);
+			}
+			
+			//TODO: if only one subtitle selected, set the cursor on the translated text box and select its text. If multiple subtitles translated, select those subtitles.
+			return true;
+		}
+		catch (TranslatorException e) { //TODO know which exceptions are originally thrown. Check if it's possible to have the second error message in the application language.
+			Console.Error.WriteLine(e);
+			BasicErrorDialog errorDialog = new BasicErrorDialog(Catalog.GetString("Could not translate the chosen subtitle."), e.Message);
+			errorDialog.Show();
+			return false;
+		}
+	}
+
+}
+
+}
diff --git a/src/GnomeSubtitles/Core/EventHandlers.cs b/src/GnomeSubtitles/Core/EventHandlers.cs
index f51b264..8261c05 100644
--- a/src/GnomeSubtitles/Core/EventHandlers.cs
+++ b/src/GnomeSubtitles/Core/EventHandlers.cs
@@ -1,6 +1,6 @@
 /*
  * This file is part of Gnome Subtitles.
- * Copyright (C) 2006-2010 Pedro Castro
+ * Copyright (C) 2006-2011 Pedro Castro
  *
  * Gnome Subtitles is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -348,6 +348,14 @@ public class EventHandlers {
 	public void OnToolsSetTranslationLanguage (object o, EventArgs args) {
 		Base.Dialogs.Get(typeof(SetTranslationLanguageDialog)).Show();
 	}
+		
+	public void OnToolsTranslateText (object o, EventArgs args) {
+		Base.CommandManager.Execute(new TranslatorCommand(true));
+	}
+		
+	public void OnToolsTranslateTranslation (object o, EventArgs args) {
+		Base.CommandManager.Execute(new TranslatorCommand(false));	
+	}
 	
 	
 	/*	Help Menu */
diff --git a/src/GnomeSubtitles/Ui/Menus.cs b/src/GnomeSubtitles/Ui/Menus.cs
index d1d1c60..d2506a3 100644
--- a/src/GnomeSubtitles/Ui/Menus.cs
+++ b/src/GnomeSubtitles/Ui/Menus.cs
@@ -1,6 +1,6 @@
 /*
  * This file is part of Gnome Subtitles.
- * Copyright (C) 2006-2010 Pedro Castro
+ * Copyright (C) 2006-2011 Pedro Castro
  *
  * Gnome Subtitles is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -141,6 +141,8 @@ public class Menus {
 	/// <param name="sensitivity">Whether the items are set sensitive.</param>
 	private void SetOneSelectionDependentSensitivity (bool sensitivity) {
 		SetVideoSelectionDependentSensitivity(sensitivity);
+		SetSensitivity(WidgetNames.ToolsTranslateText, sensitivity && Base.Document.IsTranslationLoaded);
+		SetSensitivity(WidgetNames.ToolsTranslateTranslation, sensitivity && Base.Document.IsTranslationLoaded);
 	}
 	
 	private void SetSubtitleCountDependentSensitivity (int count) {
@@ -234,6 +236,10 @@ public class Menus {
 		SetSensitivity(WidgetNames.FileTranslationSaveAs, sensitivity);
 		SetSensitivity(WidgetNames.FileTranslationClose, sensitivity);
 		SetSensitivity(WidgetNames.ToolsSetTranslationLanguage, sensitivity);
+		
+		bool oneSubtitleSelected = (Core.Base.Ui.View.Selection.Count == 1);
+		SetSensitivity(WidgetNames.ToolsTranslateText, sensitivity && oneSubtitleSelected);
+		SetSensitivity(WidgetNames.ToolsTranslateTranslation, sensitivity && oneSubtitleSelected);
 		SetViewVideoSubtitlesSensitivity();
 	}
 		
diff --git a/src/GnomeSubtitles/Ui/WidgetNames.cs b/src/GnomeSubtitles/Ui/WidgetNames.cs
index ab9d506..f2cea75 100644
--- a/src/GnomeSubtitles/Ui/WidgetNames.cs
+++ b/src/GnomeSubtitles/Ui/WidgetNames.cs
@@ -89,6 +89,8 @@ public class WidgetNames {
 	public const string ToolsAutocheckSpelling = "toolsAutocheckSpelling";
 	public const string ToolsSetTextLanguage = "toolsSetTextLanguage";
 	public const string ToolsSetTranslationLanguage = "toolsSetTranslationLanguage";
+	public const string ToolsTranslateText = "toolsTranslateText";
+	public const string ToolsTranslateTranslation = "toolsTranslateTranslation";
 
 
 	/* Toolbar */
diff --git a/src/SubLib/Core/Translator.cs b/src/SubLib/Core/Translator.cs
new file mode 100644
index 0000000..de8ee35
--- /dev/null
+++ b/src/SubLib/Core/Translator.cs
@@ -0,0 +1,133 @@
+/*
+ * This file is part of SubLib.
+ * Copyright (C) 2011 Pedro Castro
+ *
+ * SubLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * SubLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using SubLib.Core.Domain;
+using SubLib.Exceptions;
+using System;
+using System.Net;
+using System.IO;
+using System.Globalization;
+using System.Text.RegularExpressions;
+using System.Text;
+
+namespace SubLib.Core {
+	/// <summary>Allows translating subtitles from one language to another using Google Translate service.</summary>	
+	public class Translator	{
+		private const string REQUEST_URL_FORMAT = "http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&{0}langpair={1}|{2}";
+		private const string REQUEST_LINE_FORMAT = "q={0}&";
+		
+		private const char SPLIT_CHAR = '\n';
+		private const char CULTURE_REPLACEE = '_';
+		private const char CULTURE_REPLACER = '-';
+		
+		private const string JSON_RESPONSE_SUCCESS = "200";
+		
+		private const string REGEX_ERROR_MATCH = @"^.*\""responseDetails\"": (.+),.*\""responseStatus\"":.*([0-9][0-9][0-9]).*$";
+		private const string REGEX_RESPONSE_MATCH = @"\""translatedText\"":\""([^\""]*)";
+		private const int REGEX_MATCH_INDEX_ONE_VALUE = 1;
+		private const int REGEX_MATCH_INDEX_TWO_VALUE = 2;
+		
+		public const int TIMEOUT = 3000;
+
+				
+		/* Public methods */
+		
+		/// <summary>Translates given text between two given languages.</summary>
+		public static string TranslateText (string inputText, string fromLanguage, string toLanguage, int timeoutMillis) {
+			// testing emptiness of the string
+			if (inputText.Trim().Length == 0)
+				return String.Empty;
+			
+			// splitting input text into single lines; line-breaks cannot be transferred to Google Translate
+			StringBuilder stringBuilder = new StringBuilder();
+			foreach (String split in inputText.Split(SPLIT_CHAR)) {
+				stringBuilder.Append(String.Format(REQUEST_LINE_FORMAT, System.Web.HttpUtility.UrlEncode(split)));
+			}
+			
+			try {
+	            //TODO 'key' parameter could be added
+				// building request URL
+				string requestUrl = String.Format(REQUEST_URL_FORMAT, stringBuilder.ToString(),				                                  
+	                CultureInfo.CreateSpecificCulture(fromLanguage.Replace(CULTURE_REPLACEE, CULTURE_REPLACER)).TwoLetterISOLanguageName.ToLowerInvariant(),
+				    CultureInfo.CreateSpecificCulture(toLanguage.Replace(CULTURE_REPLACEE, CULTURE_REPLACER)).TwoLetterISOLanguageName.ToLowerInvariant()                               
+	            );				
+	
+				// translating
+	            HttpWebRequest req = HttpWebRequest.Create(requestUrl) as HttpWebRequest;
+				req.Timeout = timeoutMillis;
+				req.Method = WebRequestMethods.Http.Get;
+                HttpWebResponse res = req.GetResponse() as HttpWebResponse;
+				
+				// processing response
+                string responseJson = new StreamReader(res.GetResponseStream()).ReadToEnd();
+
+                return ProcessJson(responseJson);
+            }
+			catch (Exception e) {
+				throw new TranslatorException(e.Message);
+            }
+        }
+		
+		
+		/* Private methods */
+		
+		/// <summary>Processes JSON response from Google Translate service.</summary>
+		private static string ProcessJson (string responseJson) {
+			// fixing english-related quote problem
+			responseJson = responseJson.Replace("\\\"", "\\u0026quot;"); 
+			
+			// matching responseStatus
+			StringBuilder stringBuilder = new StringBuilder();
+			Match match = Regex.Match(responseJson, REGEX_ERROR_MATCH, RegexOptions.IgnoreCase | RegexOptions.Compiled);
+			if (match.Success && match.Groups.Count > REGEX_MATCH_INDEX_TWO_VALUE && match.Groups[REGEX_MATCH_INDEX_TWO_VALUE].Value == JSON_RESPONSE_SUCCESS) {
+
+				// matching multi-line translated text and building output
+				bool first = true;
+	            MatchCollection matchCollection = Regex.Matches(responseJson, REGEX_RESPONSE_MATCH, RegexOptions.IgnoreCase | RegexOptions.Compiled);
+				foreach (Match textMatch in matchCollection) {
+					if (textMatch.Success && textMatch.Groups.Count > REGEX_MATCH_INDEX_ONE_VALUE) {
+						if (!first)
+							stringBuilder.Append(SPLIT_CHAR);
+							
+						stringBuilder.Append(textMatch.Groups[REGEX_MATCH_INDEX_ONE_VALUE].Value);
+						first = false;
+					}
+				}
+				
+				// replacing escaped characters that are not decoded automatically
+				stringBuilder = stringBuilder.Replace("\\u0026#39", "'");
+				stringBuilder = stringBuilder.Replace("\\u0026quot;", "\"");
+				stringBuilder = stringBuilder.Replace("\\u0026amp;", "&");
+				stringBuilder = stringBuilder.Replace("\\u0026lt;", "<");
+				stringBuilder = stringBuilder.Replace("\\u0026qt;", ">");
+				stringBuilder = stringBuilder.Replace("\\u0026amp;", "&");
+				stringBuilder = stringBuilder.Replace("\\u0026", "&");
+				stringBuilder = stringBuilder.Replace("\\u003c", "<");
+				stringBuilder = stringBuilder.Replace("\\u003e", ">");
+				stringBuilder = stringBuilder.Replace("\\\\", "\\");
+				
+				return System.Web.HttpUtility.UrlDecode(stringBuilder.ToString());
+	        }
+			else {
+				throw new Exception((match.Groups.Count > REGEX_MATCH_INDEX_TWO_VALUE ? match.Groups[REGEX_MATCH_INDEX_TWO_VALUE].Value + ": " : String.Empty) + (match.Groups.Count > REGEX_MATCH_INDEX_ONE_VALUE ? match.Groups[REGEX_MATCH_INDEX_ONE_VALUE].Value : String.Empty));
+			}
+		}
+		
+    }
+}
\ No newline at end of file
diff --git a/src/SubLib/Exceptions/TranslatorException.cs b/src/SubLib/Exceptions/TranslatorException.cs
new file mode 100644
index 0000000..c48c4de
--- /dev/null
+++ b/src/SubLib/Exceptions/TranslatorException.cs
@@ -0,0 +1,36 @@
+/*
+ * This file is part of SubLib.
+ * Copyright (C) 2011 Pedro Castro
+ *
+ * SubLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * SubLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+using System;
+
+namespace SubLib.Exceptions {
+
+/// <summary>The exception that is thrown when error in any of Translator processes <see cref="Translator" /> occurs.</summary>
+public class TranslatorException : ApplicationException {
+	private static string defaultMessage = "Translator request failed.";
+
+	public TranslatorException (string message) : base(message) {
+	}
+	
+	public TranslatorException() : base(defaultMessage) {
+	}
+
+}
+
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]