Click here to Skip to main content
Click here to Skip to main content

.NET Resource (.resx file) Translator

By , 27 Aug 2012
 

Main Window

Introduction

Apart from the default language of the application (generally English), your software should support different languages since people prefer using software with a native language interface. For the worldwide distribution of an application, you need to translate the user interface to as many languages as possible. When you do that, you can say the application is Globalized.

The first step to globalize an application is setting the Localizable property of the Windows Form to true. When you create a Forms based Windows application, there is a resource (.resx) file associated with each form. This resource file is specific to a language which contains all locale specific details of that form.

In this article, we will discuss how to generate a different language resource file from the default English resource file.

Background

I have posted an article on Globalization/Internationalization too. Before reading this article, please read: Globalization, Internationalization (I18N), and Localization using C# and .NET 2.0.

Using the Code

The text translation is based on the translation provided by the Google Translator website (http://translate.google.com).

Although the Google Translator .NET API classes are available, The usage of these APIs are not free now. So I have updated the source code to read the translated data directly from website. 

Here, we have the Translator class which basically uses the Google Translator Website to translate the text. It sends the text with appropriate parameters to the website and extracts translated text from the response. 

/// <summary>
/// Translates the given text in the given language.
/// </summary>
/// <param name="targetLanguage">Target language.</param>
/// <param name="value">Text to be translated.</param>
/// <returns>Translated value of the given text.</returns>
public static string Translate(Language targetLanguage, string text)
{
    string translatedValue = string.Empty;
    try
    {
        string languagePair = string.Format("en|{0}", targetLanguage.Value);
        // Get the translated value.
        translatedValue = TranslateText(text, languagePair);
        Trace.WriteLine(string.Format("Given Text is {0} and Target Language is {1}. Result - {2}.",
            text, targetLanguage.Name, translatedValue));
    }
    catch (Exception ex)
    {
        string errorString = "Exception while translating, please check the connectivity." + ex.Message;
        Trace.WriteLine(errorString);
        throw new WebException(errorString);
    }
    return translatedValue;
}

Once the given text is translated in the target language, the only work left is creating a .resx file and adding the text into that file. The code given below performs this task:

/// <summary>
/// Translates the given resx in the specified language. new language resx
/// file will be created in the same folder and with the same name suffixed with
/// the locale name.
/// </summary>
/// <param name="targetLanguage">Language in which text to be translated.</param>
/// <param name="resxFilePath">Source resx file path</param>
public static void Write(Language targetLanguage, 
       string resxFilePath, bool onlyTextStrings)
{
    if (string.IsNullOrEmpty(resxFilePath))
    {
        throw new ArgumentNullException(resxFilePath, 
          "Resx file path cannot be null or empty");
    }
    if (targetLanguage == null)
    {
        throw new ArgumentNullException(targetLanguage, 
                  "Target Language cannot be null");
    }
    using (ResXResourceReader resourceReader = new ResXResourceReader(resxFilePath))
    {
        //string locale = targetLanguage.ToString().Substring(0, 2).ToLower();
        string locale = targetLanguage.Value.ToLower();
        #region Create locale specific directory.
        //string outputFilePath = Path.Combine(Path.GetDirectoryName(ResxFilePath),
            locale);
        //if (!Directory.Exists(outputFilePath))
        //{
        // Directory.CreateDirectory(outputFilePath);
        //} 
        #endregion
        // Create the required file name with locale.
        string outputFilePath = Path.GetDirectoryName(resxFilePath);
        string outputFileName = Path.GetFileNameWithoutExtension(resxFilePath);
        outputFileName += "." + locale + ".resx";
        outputFilePath = Path.Combine(outputFilePath, outputFileName);
        // Create a resx writer.
        using (ResXResourceWriter resourceWriter = new ResXResourceWriter(outputFilePath))
        {
            foreach (DictionaryEntry entry in resourceReader)
            {
                string key = entry.Key as string;
                // Check if the Key is UI Text element.
                if (!String.IsNullOrEmpty(key))
                {
                    if (onlyTextStrings)
                    {
                        if (!key.EndsWith(".Text"))
                        {
                            continue;
                        }
                    }
                    string value = entry.Value as string;
                    // check for null or empty
                    if (!String.IsNullOrEmpty(value))
                    {
                        // Get the translated value.
                        string translatedValue = Translator.Translate(targetLanguage,
                            value);
                        // add the key value pair.
                        resourceWriter.AddResource(key, translatedValue);
                    }
                }
            }
            // Generate resx file.
            resourceWriter.Generate();
        }
    }
}

Since the text translation happens at the Google website, it takes time to fetch the text and get the translated text. I have used the BackgroundWorker class to do this job. The BackgroundWorker initiates the process by Binding the channel and then sending and receiving the text with the specified languages.

The BackgroundWorker class allows you to run an operation on a separate, dedicated thread. Time-consuming operations like downloads and database transactions can cause your user interface (UI) to seem as though it has stopped responding while they are running. When you want a responsive UI and you are faced with long delays associated with such operations, the BackgroundWorker class provides a convenient solution.

private void myStartButton_Click(object sender, EventArgs e)
{
    *                 
    *            
    myBackgroundWorker.RunWorkerAsync(languages);        
    *
    *
}
void myBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    BackgroundWorker worker = sender as BackgroundWorker;
    Language[] languages = e.Argument as Language[];
    PerformTranslation(languages, worker, e, myOnlyTextCheckBox.Checked);
}

Once you hit the Start Translation button on the UI, the BackgroundWorker starts the text translation. In case you want to abort the background process, call the CancelAsync() method and then check for the CancellationPending flag on the BackgoundWorker instance, and set DoWorkEventArgs - e.Cancel to true.

/// <summary>
/// Perform translation for each selected language for all selected resx files.
/// </summary>
/// <param name="languages">selected languages.</param>
/// <param name="worker">BackgroundWorker instance.</param>
/// <param name="e">DoWorkEventArgs.</param>
/// <param name="onlyTextStrings">true; to convert only '.Text' keys valus.</param>
private void PerformTranslation(Language[] languages, 
        BackgroundWorker worker, DoWorkEventArgs e, bool onlyTextStrings)
{
    int totalProgress = 0;
    foreach (string file in mySelectedFiles)
    {
        foreach (Language targetLanguage in languages)
        {
            if (worker.CancellationPending)
            {
                e.Cancel = true;
            }
            else
            {
                ResxWriter.Write(targetLanguage, file, onlyTextStrings);
                totalProgress++;
                worker.ReportProgress(totalProgress);
            }
        }
    }
}

There could be two types of .resx files. When you set the Localizable property to true, the entire information of the form (specific to a locale) is moved to the .resx file, including location, size, etc. This is an auto generated resource file which contains the entries other than the UI text strings. Another option could be a user defined resource file, which contains only strings.

In the Resx Translator application UI, you can select the 'Convert only .Text key' checkbox in order to convert only strings of the auto generated .resx files. Translating entries other than strings can cause some serious errors. In the case of the user defined resource file, you can leave this option.

There are two components in this application - the Translator and the Config UIs. In the Translator UI, you can browse and select the .resx files. In the Config UI, you can select the languages in which you want to translate your English .resx files.

Once the translation starts, the progress bar and the status bar will indicate the whole process. Hope this tool will help you to generate resource files in different languages. Wink | ;)

Points of Interest 

It reads the translated data directly from website. You should have an active internet connection to get the translated data.  

Please go through the code for better understanding of translation/extraction process. 

Sometimes during development phase you may need to verify whether the application is truly localized (all UI Text is getting translated to target language/culture) or not. You can do this by just tweaking the Translator class code. For a particular language instead of actually translating the text just return the reversed text (or prefix/suffix one character). Now after loading the application in that particular language you can easily validate all the translated texts in the UI.  

History 

  • 23/08/2012 - Modified code to read translated data from Google Translator website.  
  • 08/02/2010 - Added details of APIs.
  • 04/02/2010 - Uploaded source code.
  • 04/02/2010 - Initial post.

License

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

About the Author

Kumar, Ravikant INDIA Bangalore
Software Developer (Senior) Philips
India India
Member
Have been working with computers since the early 00's. Since then I've been building, fixing, configuring, installing, coding and designing with them. At present I mainly code windows applications in C#, WCF, WPF and SQL. I'm very interested in Design Patterns and try and use these generic principles in all new projects to create truly n-tier architectures. Also I like to code for making the User Interface very attractive...

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralA good idea, but...memberMatt T Heffron23 Aug '12 - 6:42 
This is a good idea, but automatic translation just isn't reasonable.
The translator needs context for a reasonable translation.

For example:
If I have a word to translate: "coast"
In German this comes back as: "Küste"
But the translator doesn't know if I meant "seashore" or "continue motion" (like "coast downhill on a bicycle").
The second one is entirely different.

This automatic translation might end up with quite a nonsensical UI.
All of the translations must be reviewed, and that is about the same effort as having a human do the translation in the first place.
GeneralRe: A good idea, but...memberKumar, Ravikant INDIA Bangalore23 Aug '12 - 17:35 
Yes, I agree but it saves some effort while UI testing in development phase. This kind of automatic translation can help you for initial level UI testing (required text, control size, location etc a/c to specific culture) but at later stage you should have an expert for each language. However generated resx files can be used as place holder for reviewed translation because keys will be same and a small utility can overwrite the reviewed values. Thumbs Up | :thumbsup:
Questiontry this instead..membersamthec16 Jun '12 - 3:06 
try this instead, works good:
http://resxtranslatorbot.codeplex.com/[^]
AnswerRe: try this instead..memberKumar, Ravikant INDIA Bangalore22 Aug '12 - 23:50 
I have updated the src which reads directly from Google Translator website and doesn't depend upon .NET APIs.
Hope it will help you.
GeneralMy vote of 1 [modified]membersamthec16 Jun '12 - 1:15 
Google ServiceException was unhandled by user code
 
{"[response status:403]Please use Translate v2. See http://code.google.com/apis/language/translate/overview.html"}
 
Has been changed to 4 starts as of april 2013

modified 2 Apr '13 - 10:00.

GeneralRe: My vote of 1memberKumar, Ravikant INDIA Bangalore22 Aug '12 - 23:48 
Google .NET API is removed now. This reads from webclient directly.
GeneralRe: My vote of 1memberFernandoUY23 Aug '12 - 11:07 
An error deserves a 1 vote?
GeneralMy vote of 5memberpathakpankaj14 May '12 - 20:19 
good one
GeneralRe: My vote of 5memberKumar, Ravikant INDIA Bangalore29 Jul '12 - 19:23 
Thanks for the appreciation...
GeneralMy vote of 3memberBishisht Bhatta29 Mar '12 - 21:32 
not so clear n cut explanation
GeneralRe: My vote of 3memberKumar, Ravikant INDIA Bangalore29 Jul '12 - 19:21 
Must read this - Globalization, Internationalization (I18N), and Localization using C# and .NET 2.0
GeneralMy vote of 5membermanoj kumar choubey2 Feb '12 - 0:04 
nice
Questiongoogle translate is not free anymore,memberwei10000014 Jan '12 - 5:20 
also v1 is deprecated, too bad.
AnswerRe: google translate is not free anymore,memberHexadigm Systems27 Jan '12 - 5:55 
Microsoft Translator is still free, at least up to 2MB of download activity each month at this writing (more than enough for most). Beyond that you have to pay but the service used to be completely free as well. In any case, if you want to see the Microsoft Translator in action, check out my professional resx localization tool. You can translate all the strings in your solution using Microsoft Translator, assuming you're not going to rely on a human translator (the tool supports both).
GeneralRe: google translate is not free anymore,memberKumar, Ravikant INDIA Bangalore22 Aug '12 - 23:50 
I have updated the src which reads directly from Google Translator website and doesn't depend upon .NET APIs.
Hope it will help you. Smile | :)
GeneralMy vote of 4memberRajeshkumar Chavada27 Nov '11 - 19:35 
Good Article.
Very Useful Me
GeneralRe: My vote of 4memberKumar, Ravikant INDIA Bangalore29 Jul '12 - 19:19 
Thanks Buddy...
QuestionWhen One File Translate then error generatememberRajeshkumar Chavada27 Nov '11 - 19:34 
it's a good article and very useful me.
 
but when i run and translate one file then RequestUtility.cs in file generate one error
that is [response status:403]Quota Exceeded.
 
so, please tell me how to solve this error
 
Thanks in Advance,
 
Rajeshkumar Chavada (.net developer)
Ahmedabad
AnswerRe: When One File Translate then error generatememberKumar, Ravikant INDIA Bangalore5 Dec '11 - 21:30 
Google Translator APIs have some issues. Refer: http://www.passolo.com/forum/viewtopic.php?p=2944&sid=b784c485b71d80e8b094d5c4508f6ce2[^]
Question"индусcкий код"membersoad171515 Nov '11 - 1:58 
It is really true that "индуcский код" is really badSmile | :) . This project couldn't be opened freely.
QuestionGoogle translate API v2memberpnduffy11 Oct '11 - 11:06 
Can you rework this to support the new translate API v2?
AnswerRe: Google translate API v2memberKumar, Ravikant INDIA Bangalore16 Nov '11 - 21:43 
Though not getting enough time, will try to update the src D'Oh! | :doh:
AnswerRe: Google translate API v2memberKumar, Ravikant INDIA Bangalore22 Aug '12 - 23:53 
I have updated the src which reads directly from Google Translator website and doesn't depend upon .NET APIs.
Hope it will help you. Smile | :)
GeneralRe: Google translate API v2memberpnduffy23 Aug '12 - 4:31 
Thanks! I'll give it a try.
GeneralGetting error in VS2008memberMizan Rahman23 May '11 - 1:48 
Hi,
 
I get this error when I ran this tool:
ResX file Could not find a part of the path 'C:\Samples\ResxTranslator\ResxTranslator\ResxTranslator\bin\resources\errorstate1.bmp'. Line 288, position 5. cannot be parsed.
In the main resource file (Resource.resx), line 288 looks like this:
  <data name="ErrorState1" type="System.Resources.ResXFileRef, System.Windows.Forms">
    <value>..\resources\errorstate1.bmp;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
  </data>
 
And for somreason it thinks that the relative path is relative to the ResxTranslator.exe
 
It breaks at line 59 of your source code of ResxWriter.cs:
                    foreach (DictionaryEntry entry in resourceReader)
Any suggestions?
 
Thank you.
GeneralRe: Getting error in VS2008memberinvaders@earthling.net10 Jan '12 - 13:21 
Since you and many others may have relative paths in their .resx files that would cause that error you can do the following to force the proper resolving of the base path for the resource reader object.
 
In ResxWriter before the foreach for the resourceReader find:
 
string outputFilePath = Path.GetDirectoryName(resxFilePath);
string outputFileName = Path.GetFileNameWithoutExtension(resxFilePath);
 
Now add the following between those 2 lines:
resourceReader.BasePath = outputFilePath;
 
It should now look like this:
string outputFilePath = Path.GetDirectoryName(resxFilePath);
resourceReader.BasePath = outputFilePath;
string outputFileName = Path.GetFileNameWithoutExtension(resxFilePath);
 
Now you can avoid crashing for when using relative paths in .resx with this tool.
Good luck,
Vern

GeneralRe: Getting error in VS2008memberKumar, Ravikant INDIA Bangalore12 Jan '12 - 21:58 
Exactly, Thanks Vern... Will update the src soon.
GeneralTranslate Language using resource file to my web site.memberMember 47502133 May '11 - 1:16 
Hi,
 
I have created web site and need to translate to different language using resource file.
(Like: Hindi, France, Germen etc,.
 
Kindly help me in this matter.
 
Thanks,
M.S.Praveen

GeneralRe: Translate Language using resource file to my web site.memberKumar, Ravikant India Bangalore9 May '11 - 0:03 
Hope this will help you - Globalization, Internationalization (I18N), and Localization using C# and .NET 2.0[^]
Generalthanks for sharing - have 5memberPranay Rana30 Jan '11 - 18:54 
thanks for sharing

GeneralMy vote of 4memberdfigure29 Dec '10 - 5:23 
Nice little simple tool for developers. If it could replace Passolo I'd give you a 5. Wink | ;-) Good job.
GeneralDealing with legacy code pagesmemberdfigure29 Dec '10 - 5:20 
First of all thanks for your resource translator. I was able to use it to test an application which helped me find a bug in my code.
 
One thing I found out was that there are legacy code pages for Chinese. Your application, because of google API, outputs zh-CN. My application uses zh-CHS. So I made a simple modification to allow for legacy chinese and I'm a happy camper. Once again thanks for the cool tool. Wink | ;)
GeneralRe: My vote of 2memberKumar, Ravikant India Bangalore26 Dec '10 - 22:48 
Sorry for your disappointment. Frown | :( It’s just a tool which is developed in hurry for primary translation of our product UI strings. Because of the time constraint I have not added any code for security, performance and exception handling etc. But I believe it can be added easily if required. Otherwise it works fine as long as the input files are okay.
QuestionRe: My vote of 2memberChesnokov Yuriy26 Dec '10 - 23:51 
yes, it needs refactoring.
can you help me with the http://www.codeproject.com/Messages/3711384/My-vote-of-1-poor-exception-handling-and-code-layo.aspx[^]
I'm using just ordinary windows forms.
Чесноков

AnswerRe: My vote of 2memberKumar, Ravikant India Bangalore27 Dec '10 - 18:29 
Agreed... Smile | :) I will try to update the src soon... Thank you.
QuestionPoor exception handling and code layout, how do you handle ResXResourceReader exceptions?memberChesnokov Yuriy26 Dec '10 - 21:51 
It is nice you pointed out to the google translation API application but it took me to debug into your code to get the error Frown | :(
If you formatted it properly and provided exception handling it might be good application.
 
There is no try-catch in background worker, and any exception immediatly gets you out of the thread to the message box without any hint to the error.
 
I found the probelm is in using (ResXResourceReader resourceReader = new ResXResourceReader(resxFilePath)). If the resource reading was not correct there is no exception is thrown. Thus foreach (DictionaryEntry entry in resourceReader) line throw away you from the thread.
 
What is the reason for that message in ResXResourceReader object?
I have the same with resgen.exe also.
 
Message = "parsing file ResX Type System.Windows.Forms.DockStyle, System.Windows.Forms, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089 in data line 121, position 4 was not found. Line 123, position 5. impossible. "
Чесноков

GeneralA really helpful toolmemberJax000000999999920 Aug '10 - 0:05 
This is a great tool. I've spent hours manually updating resx files and was about to write an app like this to do it for me, you just saved me the trouble - thanks!
 
Would be great if this was a VS plugin!
GeneralGoogle API Access Noticememberpeterfoo10 Aug '10 - 3:54 
I am translating a rather large file using this tool. However I am getting an exception now.
 
[response status:403]Suspected Terms of Service Abuse. Please see http://code.google.com/apis/errors
 

Perhaps it has something to do with the size of the file I am translating?
GeneralRe: Google API Access NoticememberKumar, Ravikant India Bangalore10 Aug '10 - 6:33 
I dint run the app but I suspect change in GoogleTranslator API.
Check the given website for any change in APIs.
Also test with some small files first then your actual one or
Divide your files and then translate.
GeneralGood toolmemberjcruz20 Jun '10 - 11:25 
This application wirk very well for me.
Thanks for share this.
QuestionWhy use this approach and not the API?memberTvdHeuvel12 Feb '10 - 13:20 
Why use this approach and not the API?
just wondering..
AnswerRe: Why use this approach and not the API?memberKumar, Ravikant India Bangalore12 Feb '10 - 17:33 
There is no problem in using the APIs directly in your app but
1. You should have internet connectivity while running your application.
2. Reading the strings from google website will be slow, and hence the application performance will be affected.
3. Every time you have to read the strings while initializing a Form (e.g.- DialogBox).
4. What is the use of resx files then...?
You cannot improve the translation since it will be totally dependent upon google translation accuracy.
5. If the APIs will stop working (say, due to change in google interface) then your application will be no longer useful.
 
This tool can be basically used to translate the english resx files into some other language. Once you have the translated resx files, you app will work without such constraints.
GeneralAccuracy of Google translationsmembertystent10 Feb '10 - 7:02 
I have read various forum responses lately commenting on the quality of Google translations. As an automated service, it will usually get good results if you are translating well-formed phrases of commonly used (and properly spelled) words in the source language; it will probably not be as accurate for slang words, misspellings, short phrases, etc.
 
But this tool will provide a good first version, and you can add a phrase on your web pages asking your customers to help improve the translations for you. And of course you must be willing to manually update your translated .resx files with their suggestions.
 
Perhaps this tool can be extended to be able to mark certain translated phrases in the target language as unmodifiable once you have done an update (perhaps also in this tool).
 
Anyway it's a good start!

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 27 Aug 2012
Article Copyright 2010 by Kumar, Ravikant INDIA Bangalore
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid