Search code examples
silverlight-4.0localizationsilverlight-oob

Silverlight localization from database, not resx


I have a Silverlight 4 OOB application which needs localizing. In the past I have used the conventional resx route but I have been asked to follow the architecture of an existing winforms app.

All the strings are currently stored in a database - I use a webservice to pull these down and write them into a local Effiproz Isolated Storage database. On Login I load a Dictionary object with the language strings for the users language. This works fine.

However, I want to automate the UI localization (the WinForms app does it like this): Loop through all the controls on the page and look for any Textblocks - if there is a text property I replace it with the localized version. If the text is not found, then I WRITE the string to the database for localization.

This works ok on simple forms but as soon as you have expanders/scrollviewers and content controls then the VisualTree parser does not return the children of the controls as they are not necessarily visible (see my code below). This is a known issue and thwarts my automation attempt.

My first question is: Is there a way of automating this on page load by looping through the complex (non-visual) elements and looking up the value in a dictionary?

My second question is: If not, then is the best way of handling this is to load the strings into an app resource dictionary and change all my pages to reference it, or should I look into generating resx files, either on the server (and package it with the app as per normal) or on the client (I have the downloaded strings, can I make and load resx files?)

Thanks for any pointers.

Here is my existing code that does not work on collapsed elements and complex content controls:

  public void Translate(DependencyObject dependencyObject)
    {
        //this uses the VisualTreeHelper which only shows controls that are actually visible (so if they are in a collapsed expander they will not be returned). You need to call it OnLoaded to make sure all controls have been added
        foreach (var child in dependencyObject.GetAllChildren(true))
        {
            TranslateTextBlock(child);
        }
    }

private void TranslateTextBlock(DependencyObject child)
{
    var textBlock = child as TextBlock;
    if (textBlock == null) return;

    var value = (string)child.GetValue(TextBlock.TextProperty);
    if (!string.IsNullOrEmpty(value))
    {
        var newValue = default(string);
        if (!_languageMappings.TryGetValue(value, out newValue))
        {
            //write the value back to the collection so it can be marked for translation
            _languageMappings.Add(value, string.Empty);
            newValue = "Not Translated";
        }
        child.SetValue(TextBlock.TextProperty, newValue);
    }
}

Then I have tried 2 different approaches:

1) Store the strings in a normal dictionary object 2) Store the strings in a normal dictionary object and add it to the Application as a Resource, then you can reference it as

TextBlock Text="{Binding Path=[Equipment], Source={StaticResource ResourceHandler}}" 



App.GetApp.DictionaryStrings = new AmtDictionaryDAO().GetAmtDictionaryByLanguageID(App.GetApp.CurrentSession.DefaultLanguageId);

Application.Current.Resources.Add("ResourceHandler", App.GetApp.DictionaryStrings);

//http://forums.silverlight.net/forums/p/168712/383052.aspx


Solution

  • Ok, so nobody answered this and I came up with a solution.

    Basically it seems that you can load the language dictionary into your global resources using

    Application.Current.Resources.Add("ResourceHandler", App.GetApp.DictionaryStrings);
    
    <TextBlock Text="{Binding [Equipment], Source={StaticResource ResourceHandler}}" />
    

    and then access it like a normal StaticResource. We have the requirement of noting all our missing strings into a database for translation - for this reason I chose to use a Converter that calls a Localise extension method (so it can be done on any string in the code behind) which then looks up the string in the Dictionary (not the resource) and can do something with it (write it to a local DB) if it does not exist.

    Text="{Binding Source='Logged on User', Converter={StaticResource LocalizationConverter}}"/>
    

    This method works ok for us.