Martin Normark's blog

Posted on by Martin Normark


A long time ago, I wrote a blog post about how to translate text in C# using Google Translate. Since then, an official AJAX API has been released, which is a much better solution. Reading the comments of the blog post indicates that the code was broken quite quickly!

Google’s AJAX Language API, also includes functionality to detect language from a given string. All you can do on translate.google.com you can do via the API. Reading the class reference for the Translation API, for Flash and other Non-Javascript environments gives us the information we need to perform translations from C#. Most important is the URL and the parameters required and accepted.

Another very important thing we can read in the documentation is this:

Applications MUST always include a valid and accurate http referer header in their requests

We must remember to add a valid referrer when making the actual call!

The response object

Google’s AJAX Language APIs uses JSON, and returns a JSON object containing 3 properties:

  • responseData
  • responseDetails
  • responseStatus

This means we need to have some C# objects that we can use to deserialize the JSON returned from Google. I’m using a generic class called GoogleAjaxResponse<T>, with T being the specific class I want the JSON to be deserialized to.

using System.Runtime.Serialization;

namespace Milkshake.Integration.Google
{
	/// Defines a response from one of Google's AJAX APIs.
	/// The type of object, being returned.
	[DataContract]
	public class GoogleAjaxResponse<T>
	{
		///
		/// Gets or sets the response data.
		///
		///
		/// The responseData from Google AJAX API Call
		///
		/// The response data.
		[DataMember(Name = "responseData", Order = 0)]
		public T ResponseData { get; set; }
	}
}

To deserialize the Translation specific JSON, I use the TranslationResponse class:

using System.Net;
using System.Runtime.Serialization;

namespace Milkshake.Integration.Google.Translate
{
	/// Defines a response from the Google AJAX Translate API.
	[DataContract]
	public class TranslationResponse
	{
		/// Initializes a new instance of the  class.
		public TranslationResponse()
		{
			this.ResponseStatus = HttpStatusCode.OK;
		}

		/// Gets or sets the translated text.
		/// The translated text.
		[DataMember(Name = "translatedText", Order = 0)]
		public string TranslatedText { get; set; }

		/// Gets or sets the response details.
		/// The response details.
		[DataMember(Name = "responseDetails", Order = 1)]
		public object ResponseDetails { get; set; }

		/// Gets or sets the response status.
		/// The response status.
		[DataMember(Name = "responseStatus", Order = 2)]
		public HttpStatusCode ResponseStatus { get; set; }
	}
}

Calling the Google AJAX Translate API

To make the actual call, we can use the HttpWebRequest and HttpWebResponse classes.

using System;
using System.Globalization;
using System.IO;
using System.Net;
using System.Web;
using System.Web.Script.Serialization;

namespace Milkshake.Integration.Google.Translate
{
	/// An API Client for the Google AJAX Translate API.
	public class TranslateApi
	{
		/// The JavaScript serializer
		private JavaScriptSerializer _Serializer = new JavaScriptSerializer();

		/// Translates the text.
		public string TranslateText(string inputText, string sourceLanguage, string destinationLanguage, string referrer, string apiKey)
		{
			string requestUrl = string.Format("http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q={0}&langpair={1}|{2}&key={3}", HttpUtility.UrlEncode(inputText), sourceLanguage.ToLowerInvariant(), destinationLanguage.ToLowerInvariant(), apiKey);

			try
			{
				using (HttpClient http = new HttpClient(requestUrl))
				{
					http.AddReferrer(referrer);
					http.ExecuteRequest();

					string responseJson = http.GetResponseString();

					GoogleAjaxResponse<TranslationResponse> translation = this._Serializer.Deserialize<GoogleAjaxResponse<TranslationResponse>>(responseJson);

					if (translation != null && translation.ResponseData != null && translation.ResponseData.ResponseStatus == HttpStatusCode.OK)
					{
						return translation.ResponseData.TranslatedText;
					}
					else
					{
						return String.Empty;
					}
				}
			}
			catch
			{
				return String.Empty;
			}
		}
	}
}

The response JSON and translated text

Doing a simple test, I translate “Hello world” from English to Danish, and I can see that it works like a charm.

image

Professional translations - even via API!
translation agency

Posted on by Martin Normark | Posted in C#, Internationalization | Tagged , ,


Posted on by Martin Normark


There’s a lot of talk on Twitter and blogs about The Ultimate Developer PC 2.0. The Ultimate Developer PC, was something started three years ago by Scott Hanselman and Jeff Atwood, when Scott wanted a new machine and went for the best of the best. Jeff Atwood came up with his take, known as The Coding Horror Ultimate Developer Rig.

The same story goes for The Ultimate Developer PC 2.0, only that now it’s Scott Hanselman and Pete Brown building their new machines with an obession of having to achieve a 7.9 score on the Windows Experience Index (WEI).

Back in August 2007 with The Ultimate Developer PC 1.0, Scott Hanselman achieved a WEI of 5.8. I guess what started the 7.9 WEI obsession, is the WEI Share website where you can show off your score, and throw down your parts list. Great idea.

Is it worth spending $3,168, to get WEI 7.9?

Looking at Scott Hanselman’s purchase, it totals $3,168. That’s a lot, but if you do get value for money – that is awesome. I have no problem with that!

But how much value are you really getting for the last $1,000? Could you make a PC that was almost as powerful for $2,000?

I built my machine back in November 2009. I had one goal: To built a kick ass machine, that didn’t break the bank completely. I’m not a gamer. I have an XBOX 360 for that. I do pick up a game occasionally, though, Starcraft 2 is in my thoughts – but I resist. Modern Warfare 2 has been lying on my PC unopened for almost two months now.

The video card must be able to power up my Dell 30” primary monitor, and my Dell 24” secondary monitor, without long wait times for repainting and such.

Here goes my parts lists including the current price found at NewEgg.com:

That totals: $1,606.99 – nearly half the price compared to Scott’s purchase.

Yeah – but what about the WEI score?

Remember the point of The Ultimate Developer PC 2.0 was to get a 7.9 WEI score? That cost $3,168?

So, what score does a machine for half the price get on the Windows Experience Index:

image

What about gaming?

I mentioned earlier, that Modern Warfare 2 has been lying on my case for 2 months. Not anymore, I installed it to see how well this machine would do. Remember, it's a <$150 video card, and I'm still able to spin up MW2 in 2560 x 1600 with full details enabled. I recorded a video on my phone, and uploaded it to YouTube. Check it out here:

Conclusion

I think that is a pretty good achievement. And remember that these parts are from November 2009! I didn’t pick the newest parts at that time, basically because I think you get a lot more value for money by picking parts that has been released for a month or two.

Posted on by Martin Normark | Posted in Hardware | Tagged


Posted on by Martin Normark


Almost a year ago, I answered a question on StackOverflow, about if there’s a better way to generate HTML e-mails in C# than using a StringBuilder and just appending strings one by one.

I think that any modern piece of software today, needs to send e-mail. Whether it being password recovery e-mails, rich reports, newsletters or anything else – being able to easily see and customize the look and feel of your e-mails is vital.

So, the worst way I could think of is having your HTML hidden away in some StringBuilder.Append() hell. The best solution (in my opinion) would be, if you could have plain old HTML files with parameters like <#FirstName#> so you could dynamically replace those at runtime.

The MailDefinition class

Luckily, there’s a class for that! If you read the comments on my answer on StackOverflow, you’ll notice that none of them was familiar with that class and it came as a bit of a surprise. It did for me too, I actually wrote my own “MailDefinition” class that did just about the same thing. A complete waste of time, and it only tells me that us, developers, are sometimes too trigger happy on the keyboard instead of doing some research first. A quick search in the MSDN documentation would have saved me some work here and there.

Using the MailDefinition class is pretty straight forward. You use the MailDefinition class to create an instance of MailMessage, which you can send straightaway:

MailDefinition md = new MailDefinition();
md.From = "test@domain.com";
md.IsBodyHtml = true;
md.Subject = "Test of MailDefinition";

ListDictionary replacements = new ListDictionary();
replacements.Add("<%Name%>", "Martin");
replacements.Add("<%Country%>", "Denmark");

string body = "
Hello <%Name%> You're from <%Country%>.
"; MailMessage msg = md.CreateMailMessage("you@anywhere.com", replacements, body, new System.Web.UI.Control());

Instead of having the HTML inside the code, you could easily have it in your database or as a file on the computer. That will also enable you to edit the look and feel of the HTML template at any time, without having to rebuild and deploy your application.

MailDefinition is located in the System.Web assembly, so don’t forget to add reference to that from your project.

Posted on by Martin Normark | Posted in C# | Tagged ,


Posted on by Martin Normark


If there’s one change I hate about IIS 7 and beyond, it is the lack of the very useful IISAPP script that was present on IIS 6 installations.

Run it from a command prompt, and you will see a list of all running worker processes, the process id and the application pool name the process is serving. Very useful if you need to shut down a single site.

As I mentioned, that script was removed in IIS 7 and beyond. I find myself constantly running IISAPP without any luck several times a week, and I’ve decided that this is a habit that I cannot leave behind.

So, to solve the problem I’ve created a small .bat file called iisapp.bat and copied it to the C:\Windows\System32 folder. The content of the iisapp.bat file is just a single line, that does exactly the same as the old IISAPP did – just using the new APPCMD in IIS 7 instead:

%SystemRoot%\System32\inetsrv\appcmd list wp

Now I can call IISAPP and see a list of running applications:

IIS Application list in IIS 7

And now my day got a whole lot better. Since the BAT file was copied to the System32 folder, there's no need to add anything to the PATH environment variable. This will work from any command prompt - just type iisapp and hit Enter.

The Integrated Programming model in IIS 7

IIS 7 comes with a bunch of improvements for developers. You can do a whole lot of exciting things even from web.config, but also from code.

To take advantage of the powerful integrated programming model, you need to set your application pool to use the Integrated pipeline mode. There's no limit to what you can do.

I found a lot of great stuff in the book called Professional IIS 7 and ASP.NET Integrated Programming and learned a lot of useful stuff.

IIS 7 (or 7.5) is still the preferred way to develop, test and deploy ASP.NET applications, and as a developer you must stay up-to-date on what your tools and framework has to offer!

Posted on by Martin Normark | Posted in IIS7 | Tagged


Posted on by Martin Normark


I love the new model validation features in System.ComponentModel.DataAnnotations. One thing I don’t like though, is that the ErrorMessageResourceName is loosely typed. The ErrorMessageResourceType, however, is a System.Type which will be strongly typed by assigning its value using the typeof(Namespace.ResourceSetType) method.

Since there’s no build-breaking reference between a resource file and the value of the ErrorMessageResourceName on all classes where you use it, I thought it would be cool to have a unit test that verifies the existence of all referenced resource keys.

Remember to add a reference to System.ComponentModel.DataAnnotations.

Code

/// <summary>
/// Verifies that all properties that are decorated with validation data-annotations, refers to 
/// an existing resource. This will make sure, that missing resources are not referenced.
/// </summary>
[TestMethod]
public void All_Properties_With_Validation_Annotations_Must_Refer_To_Existing_Resource()
{
    Assembly assembly = Assembly.Load(new AssemblyName("MyApp.Model.Namespace"));
    var types = assembly.GetTypes().Where<Type>(t => t.IsClass && !t.IsAbstract);

    foreach (var type in types)
    {
        var properties = type.GetProperties();

        foreach (PropertyInfo property in properties)
        {
            var attributes = property.GetCustomAttributes(true);

            foreach (var item in attributes)
            {
                if (item is ValidationAttribute)
                {
                    ValidationAttribute val = item as ValidationAttribute;

                    Assert.IsNotNull(val);

                    if (val.ErrorMessageResourceType != null)
                    {
                        Assert.AreNotEqual(String.Empty, val.ErrorMessageResourceName,
                            String.Format(@"Validation Error Resource specified on property:
                        {0}.{1} is empty!", type.ToString(), property.Name));

                        try
                        {
                            ResourceManager rm = new ResourceManager(val.ErrorMessageResourceType);
                            string resourceValue = rm.GetString(val.ErrorMessageResourceName);
                            Assert.IsFalse(String.IsNullOrEmpty(resourceValue),
                                String.Format(@"The value of the Validation Error Resource specified on property:
                            {0}.{1} is empty!", type.ToString(), property.Name));
                        }
                        catch (MissingManifestResourceException)
                        {
                            Assert.Fail(String.Format(@"Validation Error Resource specified on property:
                            {0}.{1} could not be found!", type.ToString(), property.Name));
                        }
                    }
                }
            }
        }
    }
}

Posted on by Martin Normark | Posted in C#, Internationalization, Unit testing | Tagged , ,


Posted on by Martin Normark


In IIS SSL sites have seemed to be limited to only one site per network interface, since you (from IIS Manager) cannot specify a host header binding on the HTTPS protocol.

It turns out, that it is only a limitation in the UI. So to have e.g. two sites with their own dedicated SSL certificate we need to add a host header binding on port 443 from either appcmd, managed code or by editing the applicationHosts.config file.

I like managed code the most, so I’ve written a small method in C# that does the trick. You need to have two SSL certificates named www.ssl1.com and www.ssl2.com installed on the machine. I just created a self signed certificate for both of them using the IIS Manager.

using System.Security.Cryptography.X509Certificates;
using Microsoft.Web.Administration;

namespace IisSsl
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ServerManager _serverManager = new ServerManager())
            {
                string siteName = "SSL2";
                string certName = "www.ssl2.com";

                X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
                store.Open(OpenFlags.ReadOnly);

                X509Certificate2 certificate = store.Certificates[0];

                Site site = _serverManager.Sites[siteName];

                if (site != null)
                {
                    site.Bindings.Add("*:443:" + certName,
                        certificate.GetCertHash(), store.Name);
                }
                store.Close();

                _serverManager.CommitChanges();
            }
        }
    }
}

Remember to add a reference to C:\Windows\System32\inetsrv\Microsoft.Web.Administration.dll in order to use the ServerManager class.

Posted on by Martin Normark | Posted in C#, IIS7 | Tagged ,


Posted on by Martin Normark


I love the new IntelliSense dialog in Visual Studio 2010 Beta 2, that ScottGu has blogged about. But when editing an ASP.NET MVC View Page in Visual Studio 2010 Beta 2, you end up fighting the IntelliSense. That is because IntelliSense and the HTML Editor doesn’t recognize *any* HTML tags.

image

Trying to add a simple paragraph tag in HTML suggests a panel (code snippet). When you finish the P tag, by typing >, the result is this:

image

Pretty annoying to fight with IntelliSense when you want to code! I thought you might be able to fix this, by deleting all the new ASP.NET specific code snippets in the “C:\Program Files\Microsoft Visual Studio 10.0\Web\Snippets\HTML\1033\ASP.NET” folder.

This actually works. Now you can actually see the ASP.NET MVC code snippets, that was hidden in the masses of the ASP.NET ones.

image

But it still has all the ASP.NET Server Controls listed. I thought I could get rid of these by removing a namespace reference to System.Web in the Pages section of Web.config. But that is not present. So I don’t know how to fix that.

Posted on by Martin Normark | Posted in ASP.NET MVC | Tagged


Posted on by Martin Normark


In some applications, it could be cool to have a feature that enabled the user to quickly get a glimpse of what people are saying on Twitter about the user or their product, service, company etc.

For instance, a service like GetSatisfaction.com has a feature just like that. They call it Overheard, and this is what it looks like:

overheard

There’s nothing like Twitter to give you feedback. I think MediaTemple felt the effect of unhappy customers on Twitter when their servers broke down, and stayed there for more than two days!

Anyway. I wanted to search from C#, and get back a DataTable. Here’s how it’s done:

    /// <summary>
    /// Searches Twitter for the specified query.
    /// </summary>
    /// <param name="query">The query.</param>
    /// <returns>Returns the search results as a DataTable</returns>
    public DataTable Search(string query)
    {
      DataTable dt = new DataTable();
      dt.Columns.Add("text");
      dt.Columns.Add("html");
      dt.Columns.Add("pubdate");
      dt.Columns.Add("id");
      dt.Columns.Add("link");
      dt.Columns.Add("authorname");
      dt.Columns.Add("authorlink");

      XDocument tweetResults = XDocument.Load(String.Format(
      "http://search.twitter.com/search.atom?q={0}", HttpUtility.UrlEncode(query)));
      XNamespace atomNS = "http://www.w3.org/2005/Atom";
      var q = from tweet in tweetResults.Descendants(atomNS + "entry")
              select new
              {
                Text = (string)tweet.Element(atomNS + "title"),
                Html = (string)tweet.Element(atomNS + "content"),
                DatePublished = DateTime.Parse((string)tweet.Element(atomNS + "published")),
                Id = (string)tweet.Element(atomNS + "id"),
                Link = (string)tweet.Elements(atomNS + "link")
                .Where(link => (string)link.Attribute("rel") == "alternate")
                .Select(link => (string)link.Attribute("href"))
                .First(),
                Author = (from author in tweet.Descendants(atomNS + "author")
                          select new
                          {
                            Name = (string)author.Element(atomNS + "name"),
                            Uri = (string)author.Element(atomNS + "uri"),
                          }).First()
              };

      foreach (var item in q)
      {
        dt.Rows.Add(item.Text, item.Html, item.DatePublished, item.Id, item.Link,
                    item.Author.Name, item.Author.Uri);
      }

      return dt;
    }

Posted on by Martin Normark | Posted in C# | Tagged


Posted on by Martin Normark


Whenever you deploy a website to IIS 7 that is not compliant with the IIS 7 integrated pipeline, you will get an error like this one:

iis7-integrated-pipeline-error

Not the great error message you get. It actually gives you the solution right away: Migrate Web.config to support the integrated pipeline. To do that, start a command prompt, and execute:

%SystemRoot%\system32\inetsrv\appcmd migrate config "test/"

After doing this, our Web.config is changed to support the IIS 7 integrated pipeline and we can see the website.

The Integrated Programming model in IIS 7

IIS 7 comes with a bunch of improvements for developers. You can do a whole lot of exciting things even from web.config, but also from code.

To take advantage of the powerful integrated programming model, you need to set your application pool to use the Integrated pipeline mode. There's no limit to what you can do.

I found a lot of great stuff in the book called Professional IIS 7 and ASP.NET Integrated Programming and learned a lot of useful stuff.

IIS 7 (or 7.5) is still the preferred way to develop, test and deploy ASP.NET applications, and as a developer you must stay up-to-date on what your tools and framework has to offer!

Posted on by Martin Normark | Posted in IIS7 | Tagged


Posted on by Martin Normark


Yesterday, I blogged about how you can use Google Translate to translate a string in C#. To make it more useful than just a simple translator, and because I need to translate some Global Resource files for an E-commerce website that I’m working on, I wanted to create a small Windows Application in C# that could read a Global Resource file (.resx) and translate it into a selected language using the method for translating a word in C# that i blogged about yesterday.

image

This is how it looks so far. You simply select the resource file you want to translate. Select the current language of the resource file in the middle box, and select the language you want to translate it to in the last box. Click Translate at it should work. The new resource file will be saved in the same location as the application itself.

Posted on by Martin Normark | Posted in C#, Internationalization | Tagged ,