How Alkemi Supports Multiple Languages



This applies to Windows and Android.

When I decided Alkemi should run in several languages, I checked out what mechanisms were available. I examined a few but ultimately decided they were not suitable.

My way of supporting multiple languages works for Windows Desktop (Winforms or WPF) and also Android (Xamarin/WPF). It's straightforward, and pretty easy to implement and maintain

When a program interacts with the user it often must display static information: button text, labels, directions, menus, error messages and so forth. In my experience, no one ever gave much thought to organizing how that information data is handled, as long as it could be accessed and modified with relative ease. We just did it and since (in development) if some text needed to be changed we just changed it.

But that way will not work well when supporting multiple languages.

So what I did was create (speaking of C#) a language interface. I could have created a base class with abstract members but I chose an interface. Six of one etc.

The interface has two kinds of members. One kind is like:

string ShowFileHelp();
string SelectAlkemizedTextFile();
string SelectOriginalTextFile();
string AutoEmailOn();
string AutoEmailOff();
string NoAlkemizedTextToCopy();
string NoEncryptedText();
string AlkemizedDataSeemsCorrupt();
string NothingInTheClipboard();
string EmailRecipients();

which are invoked when certain conditions arise and are often used in MessageBox.Show() or to indicate menu changes. For example, when someone clicks the menu item "Auto Email Off" (the default startup state) the menu item is changed to "Auto Email On" which is accomplished via the method AutoEmailOn(). Or when someone tries to encrypt but there is no data to encrypt, the program executes:

MessageBox.Show(language.NoEncryptedText());

I just went through my code and everywhere text information was displayed to the user I made that into a procedure of the form string Procedure();

There are two types of procedures needed by a language class: One is a start-up function or when the language is changed, to prepopulate the text fields, menu items, labels, etc. and the others are procedures called when there is a state change.

The start-up function is prototyped in the interface like

public void StartUp();

which in the case of Alkemi contains members like:

frm.Controls.Find("btnKeySet", true)[0].Text = "Set Key";
frm.Controls.Find("btnEmail", true)[0].Text = "Email";
frm.Controls.Find("btnExit", true)[0].Text = "Exit";
frm.Controls.Find("btnClearAll", true)[0].Text = "Clear All";
frm.Controls.Find("btnPasteKey", true)[0].Text = "Paste Key";


and so forth.

This "start-up" function is run when the program starts up or when a language switch is made.

The different language classes are declared like this:

public class English : ILanguage

the French class: French.cs is declared the same way:

public class French : ILanguage

the French start up function looks (in part) like this:

frm.Controls.Find("btnKeySet", true)[0].Text = "Définir la clé";
frm.Controls.Find("btnEmail", true)[0].Text = "Email";
frm.Controls.Find("btnExit", true)[0].Text = "Sortie";
frm.Controls.Find("btnClearAll", true)[0].Text = "tout effacer";
frm.Controls.Find("btnPasteKey", true)[0].Text = "Coller la clé";


And the Simplified Chinese start up function:

frm.Controls.Find("btnKeySet", true)[0].Text = "设定键";
frm.Controls.Find("btnEmail", true)[0].Text = "电子邮件";
frm.Controls.Find("btnExit", true)[0].Text = "出口";
frm.Controls.Find("btnClearAll", true)[0].Text = "全部清除";
frm.Controls.Find("btnPasteKey", true)[0].Text = "粘贴键";


Because all the language classes are based on the ILanguage interface, each language class will have implement all the methods set out in the ILanguage interface.

So, for instance the French.cs will have

string ShowFileHelp()
{
  return "Utilisez "Ouvrir l'original" pour lire les données du disque dans la boîte "Texte d'origine"".
}


and the Japanese.cs class will have:

string ShowFileHelp()
{
  return "オリジナルを開くを使用して、ディスクのデータを[オリジナルテキスト]ボックスにロードします。"
}


and so forth.

Besides these classes you will also need language selection and switching code

I have a field in my code:

Language language;

When the language switch is made, say, to French:

language = new French();
french.frm = this; //for ease of reference in MainControlSetting()
//which accesses members of the frm class
language.StartUp();


So when the language switch is made the field "language" is set to the new language.

The only difficult issue I had (besides trying to ensure the translations made at least minimal sense) was fitting other language's text into the limited space available on a computer (or Android) screen.

So to summarize:

Step 1: Convert all the places where controls show text data which is usually hard-coded, to methods of the form string AUsefulMethodName(). These will be Messages and other changeable data (button text which changes depending on the situation, etc.)

Step 2: If using WPF and the text is hard-coded into the XML, you can leave the text unchanged but you should run your start-up language's StartUp() code to make sure that any subsequent changes to the text are displayed.

Step 3: Create a method that sets all the menus and other visible control text. The method will be something like public void MainControlSetting() etc. It will be part of the language interface.

Step 4: Create the language.cs interface based on your "MethodName" functions.

Step 5: Create the translations. I used Google Translate but of course a better choice would be input from native speakers.

Step 6: Create the language switching code.

Once a language switch is made, invoke the MainControlSetting() method.

Copyright © 2020 Ronald Gans and Ronald Gans Software Co., Inc. All Rights Reserved.