Introduction
Localizing a forms application should be done using the .NET Framework. That is the right way to do it. But sometimes you want a quick and dirty translation, or you want to translate into a non-language like KJV ("Hittest thou this button"), Pirate ("Arrg! Click this and Die!") VallyGirl ("Like, you know, hit this button"). While the process of translating a form is fairly simple, doing it well can be a bit daunting. This class will hopefully help with that.
It works by having a list of original text, followed by the translated text. The XML file allows you to have multiple "languages", each one with a list of original and translated text.
Background
I had a very simple game that I wanted to translate into "Pirate", and found it was not trivial to do it well. In my search to see if someone had already done it, I found an article for a very basic system here. That version was lacking a fair number of things. I wanted the translation to be bundled with the app, I wanted it to use utf-8 / xml, and it wanted it to translate child controls. I wanted it to load once, save once, be a lot more robust, and a number of other things. So I decided to make my own class.
It should be noted that my code is set up not to be backwards translatable. You change the language choice and then restart the application, instead of switching from one language straight into another. The reason for this is that this class allows you to translate combined string
s:
myLabel.Text = Translate("You have ") +
myNumber.ToString() + " " + Translate("apples");
These combined string
s "You have 5 apples" cannot be reversed by looking up the string
it was translated into.
Using the Code
You should be able to create an empty class in your project and paste the contents of this class into it. Make sure the "namespace
" is correct (use the namespace you have defined on all your other classes / forms).
At the top of the class is a section of variables you should change.
DefaultLanguage
: This is the default language; the one the form was written/designed/coded in. When the translator is first run, it will make a set of translations for everything on the form under the "DefaultLanguage
" language. ProjectResourceString
: This is used to find the translation resource inside your project (if you bundle the translation inside your program). It should be your namespace, followed by the string
".Properties.Resources
". This allows us to take the translation XML file and bundle it within the project. ResourceName
: This is the "filename" of the XML resource file. When you add the translation XML file to your project, this is the resulting name (usually the filename without the .xml at the end). PromptForLang
: Set this to true
if you want an initial window popping up when you run your application, asking for the initial language to use. firstrun
: This skips the code that looks for a resource file bundled in with your program. If it is set to "true
", it loads and saves to the "SavePath
". It is for use when you are building your translation. Set it to "false
" when you want it to only use the resource inside your program. AddLanguage
When you want to add a new language to the system. Put the language name here (AddLanguage="Pirate"; SaveWhenDone=true;
) and run your program. You should end up with an XML file with all the translation string
s needed to make your first translation. SavePath
: This is the file name that gets used, hopefully only when you are developing your translation. When you are done, you want to add that file as a resource to your program, set FirstRun=false
, and turn off "SaveWhenDone
."
The class is set up to have one localizer class for the whole project. That really just cuts down on needing to load/parse the file. For my project, I had a class of "game settings" that I passed to every form / class that needed it. I have my localizer as a public
variable in that.
When a program first runs, you should have something like this to do a little setup:
Localizer myTranslator = new Localizer();
private void Form1_Load(object sender, EventArgs e)
{
List<string> TheLanguages = myTranslator.LanguageList();
string ToLanguage = Properties.Settings.Default.ChosenLanguage;
if (TheLanguages.IndexOf(ToLanguage) < 0)
ToLanguage = ""; string otherLanguage = myTranslator.SetLanguage(ToLanguage);
if(otherLanguage != ToLanguage)
{
Properties.Settings.Default.ChosenLanguage = otherLanguage;
Properties.Settings.Default.Save();
}
myTranslator.Translate(this);
}
The above code instantiates the localizer and gets the chosen language from the project settings.
Now, we have a button that translates and launches another form. You can translate the form this way (using just one translator) or pass the class into the other forms so you just have the one translation instance.
private void button1_Click(object sender, EventArgs e)
{
SubForm newform = new SubForm();
myTranslator.Translate(newform);
newform.ShowDialog();
}
Points of Interest
While it may seem simple to loop through your forms and replace text, it really does not work as well as you might expect. Languages may communicate the same things, but they do so using a different number of words. A good translation system will not be a word-for-word translation, but works by translating the idea. The issue is that you can have buttons that work fine in one language, but do not have enough space in them for the equivalent idea in the translated language.
The text-based translation programs I have worked with used a very similar system as the one in this class; replacing one phrase with the equivalent in another language. But GUI based translations should be more complex, taking into consideration fonts, size of components, and the like. The Microsoft translation system also takes into consideration localization of numbers (using commas instead of periods where they are used in other languages/currencies.)
Where this particular class works well, is when you have nonstandard languages, like "Pirate."
History
- 7th December, 2014: Initial version