Click here to Skip to main content
15,115,504 members
Articles / Programming Languages / C#
Article
Posted 13 Dec 2010

Tagged as

Stats

24.9K views
380 downloads
11 bookmarked

Neo Localizer

Rate me:
Please Sign up or sign in to vote.
4.40/5 (4 votes)
13 Dec 2010GPL34 min read
A possible approach to achieve software localization

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.

NeoLocalizerEditor_a.png

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).

XML
<?xml version="1.0" standalone="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:

C#
//Simple Translation of control
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:

C#
//Programmatically Translation of Form controls text
foreach (System.Windows.Forms.Control control in controls)
{
  //It’s doesn’t work fine in cases of translation "on the fly"
  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.

C#
//Programmatically Translation of Form controls text

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.

C#
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);
    }
  }

NeoLocalizerEditor_b.png

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.

ASP.NET
//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>

testwebnet.png

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

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

Share

About the Author

Martin Garcia
Software Developer (Senior) BrisConnections Operations Pty Ltd
Australia Australia
Senior Software Developer on Microsoft and Unix-like environments. Senior Data Analyst and Database Developer in particular on the MSSQL and MySQL platforms

Comments and Discussions

 
QuestionExtra Localization Pin
Z@clarco22-Feb-17 1:03
MemberZ@clarco22-Feb-17 1:03 
PraiseExactly what I was looking for Pin
dragonfly.672-Feb-17 3:18
Memberdragonfly.672-Feb-17 3:18 
GeneralIssues with this approach Pin
Grant Frisken21-Dec-10 12:28
MemberGrant Frisken21-Dec-10 12:28 
GeneralRe: Issues with this approach Pin
Martin Garcia22-Dec-10 19:34
MemberMartin Garcia22-Dec-10 19:34 
GeneralRe: Issues with this approach Pin
Grant Frisken23-Dec-10 12:36
MemberGrant Frisken23-Dec-10 12:36 
GeneralGoogle docs Pin
ik_never20-Dec-10 12:07
Memberik_never20-Dec-10 12:07 
GeneralRe: Google docs Pin
Martin Garcia22-Dec-10 19:27
MemberMartin Garcia22-Dec-10 19:27 
GeneralMy vote of 4 Pin
marisks13-Dec-10 19:34
Membermarisks13-Dec-10 19:34 
This looks good, but needs one important improvement. You could implement resource provider for better usage in .NET. Here is example http://www.codeproject.com/KB/aspnet/Custom_Resource_Provider.aspx
GeneralMy vote of 3 Pin
Toli Cuturicu13-Dec-10 15:02
MemberToli Cuturicu13-Dec-10 15:02 
GeneralRe: My vote of 3 Pin
soup14-Dec-10 7:07
Membersoup14-Dec-10 7:07 
GeneralRe: My vote of 3 Pin
Martin Garcia22-Dec-10 19:24
MemberMartin Garcia22-Dec-10 19:24 
GeneralRe: My vote of 3 Pin
Toli Cuturicu23-Dec-10 14:27
MemberToli Cuturicu23-Dec-10 14:27 

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.