Click here to Skip to main content
15,885,244 members
Articles / Programming Languages / Visual Basic

RESX Translation Tool

Rate me:
Please Sign up or sign in to vote.
4.64/5 (8 votes)
18 Mar 2009CPOL2 min read 47.5K   1.5K   52   4
A C# tool for localizing .NET projects.

Introduction

Although English is an international language, many people prefer to use software with a native language interface. So, if you want to make your software very popular among people from different countries, you will need to translate the user interface to as many languages as possible. But, it should be mentioned here that localizing software is hard work. This RESX translation tool helps to avoid a rut and to automate some operations.

This tool is based on Translation Web Service in C# by Matthew Brealey, and RESX to XLS conversion for multi-language support by Marco Roello.

Background

There are a lot of multi-language translation Web Services such as Google, Babelfish, Yandex, and others. Of course, computer translation is not as good as human translation, but it helps to quicken localizing.

Code

The translator performs the following actions:

  1. Generates the POST request to the translation Web Service (Google)
  2. Receives the human-readable response from the Web Service
  3. Parses the response using Regular Expressions
  4. Returns the translation

The function Translate implements these actions:

C#
public string Translate(string text, TranslateDir dir)
{
    HttpWebRequest request = 
      (HttpWebRequest)WebRequest.Create(TranslateUrl);

    // Encode the text to be translated
    string postSourceData = GetPostSourceData(text, dir);

    request.Method = "POST";
    request.ContentType = "application/x-www-form-urlencoded";
    request.ContentLength = postSourceData.Length;
    request.UserAgent = "Mozilla/4.0 (compatible; " + 
                        "MSIE 6.0; Windows NT 5.1)";

    HttpWebResponse response;
    
    try
    {
        using (Stream writeStream = request.GetRequestStream())
        {
            byte[] bytes = Encoding.UTF8.GetBytes(postSourceData);
            writeStream.Write(bytes, 0, bytes.Length);
            writeStream.Close();
        }
        response = (HttpWebResponse)request.GetResponse();
    }
    catch (Exception ex)
    {
        return ex.Message;
    }
    StreamReader readStream = 
      new StreamReader(response.GetResponseStream(), Encoding.UTF8);
    string page = readStream.ReadToEnd();
    response.Close();

    Regex reg = new Regex(RegexpResult, RegexOptions.IgnoreCase);
    Match m = reg.Match(page);
    string s;
    if (m.Success)
    {
        s = m.Result("${text}");
    }
    else s = "regexp error";

    return s;
}

protected override string RegexpResult
{
    get { return @"<div[^>]+id=result_box[^>]+" + 
                 @">(?<text>[^>]+)</div>"; }
}

The function GetPostSourceData generates the body for posting the request, which passes the text and the language option to the Google Translate engine. The Google response is human-readable, and the translation is put into the div with id = result_box.

The DataSet consists of two tables: Resx and ResxLocalized. These tables are filled with data from the loaded *.resx files. The function TranslateDataSet fills the ResxLocalized table with the translated values. To avoid additional delays caused by Web Service requests, this function fills only the unfilled cells.

First, select all the unfilled records into the enumeration query using LINQ. Then, find if any value is equal to the current one and has been already translated. Otherwise, send the translation request to the Google Translate Web Service.

C#
private void TranslateDataSet(ResxData rd)
{
    GoogleTranslator gt = new GoogleTranslator();
    DataTable resxs = rd.Resx;
    DataTable resxlocs = rd.ResxLocalized;
    int count = 0;
    int amount;

    var query =
        from resx in resxs.AsEnumerable()
        join resxloc in resxlocs.AsEnumerable()
        on resx.Field<int32 >("ID") equals
        resxloc.Field<int32 >("ParentID")
        where resxloc.Field<string >("Value") == null || 
              resxloc.Field<string >("Value") == ""
        select new
        {
            Value = resx.Field<string >("Value"),
            Culture = resxloc.Field<string >("Culture"),
            Translation = resxloc.Field<string >("Value"),
            LocID = resxloc.Field<int32 >("ID")
        };
    string translation;
    this.BeginInvoke((MethodInvoker)delegate()
    { progressBar1.Maximum = query.Count() + 1; progressBar1.Value = 1; });

    if (!query.Any())
    {
        this.BeginInvoke((MethodInvoker)delegate()
            { words.Text = "0 of " + query.Count().ToString(); });
    }
    amount = query.Count();
    foreach (var record in query)
    {
        var enumer = query.Where(r => r.Value == record.Value && 
                                 r.Translation != "" && 
                                 r.Translation != null && 
                                 r.Culture == record.Culture);
        count++;
        
        this.BeginInvoke((MethodInvoker)delegate()
        { words.Text = count.ToString() + " of " + amount.ToString(); });
        if (enumer.Any())
        {
            translation = enumer.First().Translation;
        }
        else
        {
            translation = gt.Translate(record.Value, 
               new TranslateDir("auto", record.Culture.Substring(0, 2)));
        }

        this.BeginInvoke((MethodInvoker) delegate() { progressBar1.Value++; });
        resxlocs.Rows.Find(record.LocID).SetField<string >("Value", translation);
    }
}

Points of Interest

There are some ways to work with Excel files in C#. They are:

  • Early binding COM
  • Late binding COM
  • Using the OLE DB provider

Only the last way doesn't demand Excel to be installed. The following code shows how to get and set values in Excel worksheets:

C#
private void SetCell(int row, int col, OleDbConnection con, string value)
{
    string c = Convert.ToChar((Convert.ToInt16('A') + col - 1)).ToString();
    string r = row.ToString();

    OleDbCommand cmd = new OleDbCommand("UPDATE [Localize$" + c + r + ":" + 
                       c + r + "] SET F1 = \"" + value + "\"", con);
    cmd.ExecuteNonQuery();
}

private string GetCell(int row, int col, OleDbConnection con)
{
    string c = Convert.ToChar((Convert.ToInt16('A') + col - 1)).ToString();
    string r = row.ToString();

    OleDbCommand cmd = new OleDbCommand("SELECT * FROM [Localize$" + 
                       c + r + ":" + c + r + "]", con);
    object ret = cmd.ExecuteScalar();
    if (ret == DBNull.Value) return null; else return (string)ret;
}

Suggestion

If anybody can contribute and implement interfaces for other translators and post the sources, I'll update the article with them.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Russian Federation Russian Federation
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMicrosoft Translator [modified] Pin
Angela Han27-Jul-09 18:42
Angela Han27-Jul-09 18:42 
GeneralVery interesting BUT IT BASICALLY DOES NOT WORK! Pin
TheCPPS24-Jun-09 5:55
TheCPPS24-Jun-09 5:55 
GeneralRe: Very interesting BUT IT BASICALLY DOES NOT WORK! Pin
Sergey Obraztsov25-Jun-09 9:43
Sergey Obraztsov25-Jun-09 9:43 
GeneralRe: Very interesting BUT IT BASICALLY DOES NOT WORK! Pin
TheCPPS27-Oct-09 5:15
TheCPPS27-Oct-09 5:15 

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.