Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C#
Article

Automatically Translate your .NET resource files with Google Translate

Rate me:
Please Sign up or sign in to vote.
4.90/5 (15 votes)
27 Oct 20064 min read 158.8K   3.8K   96   40
This article talks about building an application to translate .NET resource files like .resx and .js using Google Translate, for internationalization testing.

Image 1

Introduction

With many more applications coming up with localized versions, it is important to test if the application is ready for that after moving the resources out of the code. We have easy ways of taking strings out of the code, using resx files, satellite DLLs, and external string files (a client side js file). A quick test would be to see if all the strings are picked up from an external file. One step further would be to make sure strings in other languages (the localized ones) are displayed properly in our application. As many point out, it is important to test with European and Asian languages that use multi-byte encoding. I started off with this testing, and thought that I will copy-paste some of the strings from the web on these languages to carry out the testing. On looking around the web, I got to know about Google Translate and I was excited, and I found a way to test my application in all languages that Google supports! As I started with it, oh, it was a pain... picking up strings one by one, translating them into the different languages .... I thought it would have been good if I had an automatic tool (there is Google for that to search and tell me!).

Google returned me with a result, a way of calling Google Translate from our application, a good article and accompanying source code by Peter A. Bromberg. I thought I got what I wanted and with two lines of code I could make it work for me, but it was not so, it made me explore so many other stuff and finally made me post here so that many others could benefit and also help me refine my first C# application.

Background

I fully agree with Peter on "don't reinvent the wheel", having been with application development for over 7 years. I have gained most part of my knowledge through the web with those who had shared their two cents. I started with his code and ran it on my machine. Oops, I didn't get the result. On getting into debug mode, I found that the web scraper he was using, WebWagon, did not support proxies. Once again, it was Google time and I added the code below to use the default proxy authentication to get his code working:

C#
//Set Proxy settings to the  HttpWebRequest object  
hrqURL.Proxy = (WebProxy) GlobalProxySelection.Select;
hrqURL.Proxy.Credentials = CredentialCache.DefaultCredentials;

Then I started off with my project, translating my resx file, and upgrading it with version after versions. The first version used direct XML parsing of the resx file as I was not aware of ResXResourceReader. The second version included support for strings in a js file also as some string resources were part of the client side script. The next version removed the XML parsing and used ResXResourceReader and ResXResourceWriter. Then, to avoid the screen hanging, I added thread support and a progress bar to the application, and I have attached that version here. I am still thinking of working on a version supporting Altavista's Babelfish also. (I am very well aware that this code will break once the Google Translate page or the Babelfish page changes, but I hope it won't be in the near future and always there will be some one to break it through and get it working!)

Using the code

I have used Peter's WebWagon project as it is other than the few tweaks to get it working for me. The first one was the proxy support as I said earlier. Asian characters were not getting returned properly in his code and that was the main requirement for me to go for this application. I did a lot of trial and error to get this piece of code into WebWagon so that it supported UTF-8 encoding:

C#
//Set content type for the HTTPWebRequest object
hrqURL.Method = "GET";
hrqURL.ContentType = "application/x-www-form-urlencoded";
hrqURL.UserAgent = "Mozilla/4.0 (compatible;" + 
               " MSIE 6.0; Windows NT 5.1)";

Also, the encoding of the return data from the webpage was also missing. I added that to get the data encoded properly.

C#
//Set content type for the Response Stream reader
StreamReader srdrInput = new 
  StreamReader(hrspURL.GetResponseStream(), Encoding.UTF8);

The project I have added is straightforward; for resx files, loop through the resource strings and call Translate using the ResxResourceReader, and for js, read line by line and call Translate.

C#
// Create a ResXResourceReader for the file items.resx.
ResXResourceReader rsxr = new ResXResourceReader(fileName);
ResXResourceWriter rsxTranslated = new ResXResourceWriter(outputFileName);
// Create an IDictionaryEnumerator to iterate through the resources.
IDictionaryEnumerator id = rsxr.GetEnumerator(); 
long count=0;
object[] pct;
pct = new object[1];

// Iterate through the resources 
foreach (DictionaryEntry d in rsxr) 
{
    count++;
    int percentTranslated = (int)(count*100/totalStrings);
    pct[0]=percentTranslated;
    this.Invoke(showProgress,pct);

    string strval = d.Value.ToString();
    string translatedtxt = TranslateString(strval);
    //Add translated string to the output resource files
    rsxTranslated.AddResource(d.Key.ToString(),translatedtxt ); 
}
//store back the resx file under the chosen language option
rsxTranslated.Generate();
rsxr.Close(); 
rsxTranslated.Close();

For getting the progress bar working, I had to do some additional coding, to get the total number of strings to be translated. For js files, I went through the file till the end once, to get all the strings. For resx, I thought using XML parsing would be much faster and used a piece of code I already had in my first version:

C#
//To get the total no of resource strings to be translated (for
//progress bar) fastest way is to read resx as xml!
XmlDocument rootNode = new XmlDocument();
rootNode.Load(fileName);
XmlNodeList dataNodes = rootNode.SelectNodes("//root/data"); 
int totalStrings = dataNodes.Count;

Points of interest

As my first application in C#, I got a chance to know more stuff about: web scraping, multithreading, XML parsing, and using different UI controls, and lot more!

Reference

History

  • V1.0 - Initial update on 26 Dec 2005.
  • 27 Oct 2006 - Fixed code to work with the new changes in the Google Translate page.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
India India
Satish is an application developer working on many different technologies, VC6, VBA, CORBA, XML, ASP, ASP.NET and recently experimenting with C#. He has started looking into building rich UIs using flash, php, asp & javascript too.

Comments and Discussions

 
GeneralRe: Quit working ??? Pin
S.Badri10-Apr-07 23:08
S.Badri10-Apr-07 23:08 
GeneralThis does not work anymore !! Pin
dharmeshdshah24-Oct-06 11:39
dharmeshdshah24-Oct-06 11:39 
GeneralRe: This does not work anymore !! Pin
satishk_b27-Oct-06 2:59
satishk_b27-Oct-06 2:59 
GeneralRe: This does not work anymore !! - Fixed Pin
Satishkumar.B27-Oct-06 3:51
Satishkumar.B27-Oct-06 3:51 
Generalexcellente Pin
andrewcates3-Feb-06 6:16
andrewcates3-Feb-06 6:16 
GeneralCode for using ienumerator Pin
djohnson1493-Jan-06 8:13
djohnson1493-Jan-06 8:13 
GeneralRe: Code for using ienumerator Pin
Satishkumar.B3-Jan-06 17:58
Satishkumar.B3-Jan-06 17:58 
GeneralRe: Code for using ienumerator Pin
djohnson1494-Jan-06 4:29
djohnson1494-Jan-06 4:29 
Satish,

You are correct that the foreach construct uses IEnumerator internally. Which means that you do not need to instantiate an instance of the IEnumerator separately. You could remove the line
IDictionaryEnumerator id = rsxr.GetEnumerator();
and the foreach would operate successfully.

Dwight
GeneralRe: Code for using ienumerator Pin
Radeldudel3-Jan-06 23:16
Radeldudel3-Jan-06 23:16 
GeneralRe: Code for using ienumerator Pin
djohnson1494-Jan-06 4:33
djohnson1494-Jan-06 4:33 
GeneralWonderful Pin
Guido_d28-Dec-05 0:11
Guido_d28-Dec-05 0:11 
GeneralMany thanks for getting the fix in for Asian characters Pin
malharone27-Dec-05 16:53
malharone27-Dec-05 16:53 
Newstesting only! Pin
Roland Pibinger27-Dec-05 8:48
Roland Pibinger27-Dec-05 8:48 
GeneralGreat util Pin
seeblunt27-Dec-05 2:55
seeblunt27-Dec-05 2:55 
GeneralCool! Pin
leppie26-Dec-05 22:21
leppie26-Dec-05 22:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.