Introduction
The article describes a possible approach to achieve software localization. It could be useful in saving a programmer's or translator's time, although these roles are often covered by the same person. In the past, I adopted this solution on Microsoft (Desktop, Web) and PHP projects obtaining the expected results: saving time in localization implementation and giving a more comprehensible presentation of data to the translator.
This article introduces a simple way to adopt this approach, including working samples, and does not refer to a software component that is ready to install.
The source code in the attachment is an integral part of NeoProject application (http://neoproject.biz) and is licensed as described in the License section.
How This Approach Works
The brief process of software translation implementation can be summarized in the follows steps:
Translator
| Localizer Editor
| Dictionaries
| Localizer Engine used by Application
| End User
|
Edits each phrase in the target language
| Stores all translations in a single XML file
| Simple structure of XML file
| NeoLocalizer.dll
| <span lang="ES" style="FONT-FAMILY: 'Times New Roman','serif'; FONT-SIZE: 12pt">WindowsForm</span>
| Chooses the language once per session or "on the fly"
|
NeoLocalizer.dll NeoLocalizer.net.js
| <span lang="ES" style="FONT-FAMILY: 'Times New Roman','serif'; FONT-SIZE: 12pt">WebForms</span>
|
Translator uses Localizer Editor which shows each managed language (Culture) in columns and each phrase in rows. A Translator should gain advantages from working in a familiar layout. Also, people who haven't sufficient knowledge about the target application can be helped in understanding the meaning of translations by comparing them to a known language.
Dictionaries XML file contains all translations in a very simple structure. It’s composed of a Primary key (tag name <Code>
) and as many fields as there are managed languages (Cultures).
="1.0"="yes"
<DictionaryItems>
<DictionaryItem>
<Code>Select language</Code>
<en-AU>Select language</en-AU>
<it-IT>Seleziona la lingua</it-IT>
<es-AR>Seleccione el idioma</es-AR>
</DictionaryItem>
<DictionaryItem>
<Code>Employees list</Code>
<en-AU>Employees list</en-AU>
<it-IT>Lista Impiegati</it-IT>
<es-AR>Listado de Empleados</es-AR>
</DictionaryItem>
</DictionaryItems>
This format is straightforward to import from a Microsoft Access table or Microsoft Excel worksheet. In sample projects attached, the Dictionary file is located in the Bin directory of NeoLocalizer.Editor
and TestWeb
applications.
Localizer Engine allows a Simple translation calling the GetTranslatedText()
method. This way requires a programmer to set each text Property
of each control on the form, writing code like example code below:
this.Text = _localizer.GetTranslatedText("Sample Form");
It should make sense in forms containing a small quantity of controls, or for controls “unreachable” by programmatical translation. This example tries to reach each control (and child controls) using a nested foreach
and set Text
property passing text of control to the GetTranslatedText()
method:
foreach (System.Windows.Forms.Control control in controls)
{
control.Text = _localizer.GetTranslatedText(control.Text);
}
In the case of translations made “on the fly” (where original text of a control is overridden by translated text), the use of a cached list is needed; an array of the original text of controls on the form.
Hashtable _cacheDefaultTexts = new Hashtable();
protected void addCacheDefaultTexts(ControlCollection controls)
{
foreach (System.Windows.Forms.Control control in controls)
{
_cacheDefaultTexts.Add(control.Name, control.Text);
addCacheDefaultTexts(control.Controls);
}
}
When we apply more than one translation in the same session (“on the fly”), we need to pass to the GetTranslatedText()
method the original text of the Control.Text
property. After the first translation, we will lose this original text. For this reason, we need to store original text into a cache and then pass it to the translation method. Finally, the End User can choose to set his own language before starting a new work session or at any time during the application’s process (on the fly). It depends on the application’s design.
Points of Interest
In the sample application attached to this article, you will find ways to make translation of controls programmatically. The WindowsForm
project uses based class FormLocalizable
which the FormSample2
class is inherited from. This base class also contains a function to edit at run-time a text property placed on the WindowsForm
.
protected void setControlsEvents(System.Windows.Forms.Form form)
{
form.MouseDown += new System.Windows.Forms.MouseEventHandler
(this.Form_MouseDown_ForLocalization);
form.KeyDown += new System.Windows.Forms.KeyEventHandler
(this.KeyDown_ForLocalization);
setControlsEvents(form.Controls);
}
protected void setControlsEvents
(System.Windows.Forms.Control.ControlCollection controls)
{
foreach (System.Windows.Forms.Control control in controls)
{
if (control is System.Windows.Forms.StatusStrip)
{
setControlsEvents(((System.Windows.Forms.StatusStrip)control
as System.Windows.Forms.StatusStrip).Items);
}
else if (control is System.Windows.Forms.DataGridView)
{
setControlsEvents((System.Windows.Forms.DataGridView)control);
}
else
{
control.MouseDown += new System.Windows.Forms.MouseEventHandler
(this.Control_MouseDown_ForLocalization);
control.KeyDown += new System.Windows.Forms.KeyEventHandler
(this.KeyDown_ForLocalization);
setControlsEvents(control.Controls);
}
}
}
protected void Form_MouseDown_ForLocalization
(System.Object sender, System.Windows.Forms.MouseEventArgs e)
{
if (_keyEventArgs == null) return;
try
{
System.Windows.Forms.Form form = (System.Windows.Forms.Form)sender;
if (form.Capture && e.Button ==
System.Windows.Forms.MouseButtons.Right && _keyEventArgs.Alt)
{
string cachedText = (_cacheDefaultTexts.ContainsKey(form.Name)) ?
_cacheDefaultTexts[form.Name].ToString() : form.Text;
if (String.IsNullOrEmpty(cachedText)) return;
editDictionaryItem(cachedText);
_keyEventArgs = null;
}
}
catch { }
}
protected void editDictionaryItem(string cachedText)
{
NeoLocalizer.Editor.FormDictionaryItemNew f =
new NeoLocalizer.Editor.FormDictionaryItemNew();
if (cachedText.EndsWith(":"))
{
cachedText = cachedText.Trim(':');
}
f.DictionaryItemCode = cachedText;
f.ShowDialog(this);
if (f.DialogResult == System.Windows.Forms.DialogResult.OK)
{
if (DictionaryItemSaved != null) DictionaryItemSaved(this, EventArgs.Empty);
}
}
In the WebForm
project, translation is made on the client side using jQuery
for two important tasks; calling a local web service, which retrieves the Dictionary
array and setting each control text property using a JavaScript class and jQuery selectors.
//Default.aspx
<script type="text/javascript">
var _defaultTexts = new Array();
var _localizer = new Localizer();
$(document).ready(function ()
{
addCacheDefaultTexts();
$('#cboLanguages').change(changeLanguage);
});
function addCacheDefaultTexts()
{
$('label[id]').each(function (i, control)
{ _defaultTexts[control.id] = $('#' + control.id).text(); });
$('#grdEmployees > tbody > tr > th').each(function (i)
{ _defaultTexts['grdEmployees_col_' + i] = $(this).text(); });
}
function changeLanguage()
{
_localizer.CurrentCultureCode = $('#cboLanguages').val();
_localizer.Refresh();
translateControls();
}
function translateControls()
{
$('label[id]').each(function (i, control)
{
var cachedText = _defaultTexts[control.id];
$('#' + control.id).text(_localizer.GetTranslatedText(cachedText));
});
$('#grdEmployees > tbody > tr > th').each(function (i)
{
var cachedText = _defaultTexts['grdEmployees_col_' + i];
$(this).text(_localizer.GetTranslatedText(cachedText));
});
}
</script>
Use Sample Code
You can open the NeoLocalizer.sln
file using Microsoft Visual Studio 2008 and set as Start-up project NeoLocalizer.Editor
or WebSite TestWeb
.
Conclusion
I realized that the advantage of using a single dictionary for the whole application is to avoid the redundancy of words (the Unique Key is the Dictionary
Item) and to save time, as we don’t have to write in each form a word which already exists in another form. A classic example could be the word “Code
” or “ID
”, which could exist in many different places of an application.
Possible Things To Do
Editor tool should be improved adding new functionality such as: - Importing / exporting to Microsoft Excel Worksheet and/or Microsoft Access Table for interchange reasons with translators - Creation of new dictionary files collecting controls names from a Windows/Web Form. Moreover, I will publish the PHP version of this sample code as soon as possible.
History
- 2010.12.13 – Initial posting
Senior Software Developer on Microsoft and Unix-like environments. Senior Data Analyst and Database Developer in particular on the MSSQL and MySQL platforms